Python and FastAPI
যেহেতু আপনি Python-এ ৬ মাস ধরে কাজ করছেন, তাই আপনার বেসিক ধারণাগুলো (যেমন- ভেরিয়েবল, লুপ, ইফ-এলস) পরিষ্কার আছে বলেই আমি ধরে নিচ্ছি। এখন আপনার প্রয়োজন কোডকে আরও “Pythonic” (ক্লিন ও স্ট্যান্ডার্ড) করা এবং FastAPI-এর কোর কনসেপ্টগুলো গভীরভাবে বোঝা।
আপনার জন্য Phase 1 (প্রথম ১০টি প্রশ্ন) নিচে দেওয়া হলো, যেখানে Python-এর গুরুত্বপূর্ণ ইন্টারমিডিয়েট টপিক এবং FastAPI-এর বেসিক আর্কিটেকচার নিয়ে আলোচনা করা হয়েছে।
Phase 1: Python Essentials & Introduction to FastAPI (6-Month Experience Level)
Question 1: Why do we use Virtual Environments (venv) in Python, and how is it different from installing packages globally?
Short Answer:
A Virtual Environment (venv) is an isolated directory that contains a specific Python interpreter and its own set of installed libraries. We use it to prevent dependency conflicts between different projects. If you install packages globally, Project A (needing Django 2) and Project B (needing Django 4) will overwrite each other and break.
Detailed Answer:
As you move from beginner to professional, isolating project dependencies is rule number one.
- Isolation: When you activate a
venv, your terminal'sPATHpoints to the virtual environment's Python binary, not the global OS Python. Anypip installonly affects that specific folder. - Reproducibility: By isolating the environment, you can easily generate a
requirements.txtfile containing only the packages your specific project needs, allowing other developers to recreate the exact same environment without pulling in unnecessary global packages.
Python
# English Code Example: Virtual Environment Workflow
# 1. Create a virtual environment named "env"
# python -m venv env# 2. Activate it (Windows)
# env\Scripts\activate
# Activate it (Mac/Linux)
# source env/bin/activate# 3. Install a package specific to this project
# pip install fastapi uvicorn# 4. Save the dependencies
# pip freeze > requirements.txt
প্রশ্ন ১: Python-এ আমরা কেন ভার্চুয়াল এনভায়রনমেন্ট (venv) ব্যবহার করি, এবং এটি গ্লোবালি প্যাকেজ ইনস্টল করার থেকে কীভাবে আলাদা?
সংক্ষিপ্ত উত্তর:
ভার্চুয়াল এনভায়রনমেন্ট (venv) হলো একটি আইসোলেটেড বা বিচ্ছিন্ন ফোল্ডার, যার নিজস্ব Python ইন্টারপ্রেটার এবং লাইব্রেরি থাকে। প্রজেক্টগুলোর মধ্যে ডিপেন্ডেন্সি কনফ্লিক্ট (dependency conflict) এড়াতে আমরা এটি ব্যবহার করি। আপনি যদি গ্লোবালি প্যাকেজ ইনস্টল করেন, তবে Project A (যার Django 2 দরকার) এবং Project B (যার Django 4 দরকার) একে অপরের ফাইল ওভাররাইট করবে এবং প্রজেক্ট ক্র্যাশ করবে।
বিস্তারিত উত্তর:
বিগিনার থেকে প্রফেশনাল হওয়ার প্রথম নিয়মই হলো প্রজেক্টের ডিপেন্ডেন্সি আলাদা রাখা।
১. আইসোলেশন: যখন আপনি venv অ্যাক্টিভেট করেন, তখন আপনার টার্মিনাল গ্লোবাল Python-এর বদলে ওই ফোল্ডারের Python-কে ব্যবহার করে। ফলে pip install দিলে তা শুধু ওই প্রজেক্টেই ইনস্টল হয়।
২. রিপ্রোডিউসিবিলিটি (Reproducibility): এনভায়রনমেন্ট আলাদা থাকলে আপনি খুব সহজেই একটি requirements.txt ফাইল তৈরি করতে পারবেন, যেখানে শুধু আপনার প্রজেক্টের প্রয়োজনীয় প্যাকেজগুলোই থাকবে। এর ফলে অন্য কোনো ডেভেলপার সহজেই আপনার প্রজেক্টটি তার পিসিতে রান করতে পারবে।
Python
# বাংলা কোড এক্সাম্পল: ভার্চুয়াল এনভায়রনমেন্টের কাজ
# ১. "env" নামে একটি ভার্চুয়াল এনভায়রনমেন্ট তৈরি করা
# python -m venv env# ২. অ্যাক্টিভেট করা (Windows)
# env\Scripts\activate
# অ্যাক্টিভেট করা (Mac/Linux)
# source env/bin/activate# ৩. শুধুমাত্র এই প্রজেক্টের জন্য প্যাকেজ ইনস্টল করা
# pip install fastapi uvicorn# ৪. ডিপেন্ডেন্সিগুলো সেভ করে রাখা
# pip freeze > requirements.txt
Question 2: What are the core differences between List, Tuple, Set, and Dictionary in Python?
Short Answer:
- List (
[]): Ordered, mutable (changeable), and allows duplicates. - Tuple (
()): Ordered, immutable (cannot be changed after creation), and allows duplicates. Highly memory-efficient. - Set (
{}): Unordered, mutable, but strictly enforces unique elements (no duplicates). Highly optimized for membership testing (in). - Dictionary (
{key: value}): Unordered (ordered in Python 3.7+), mutable collection of key-value pairs where keys must be unique and immutable.
Detailed Answer:
Choosing the right data structure dictates the performance of your application.
- Immutability (Tuple): If you are returning multiple values from a function that shouldn’t be altered (like database coordinates), use a Tuple. It protects the data from accidental modification.
- Unique Data (Set): If you have a list of 10,000 email addresses and want to remove duplicates, simply convert it to a Set
set(emails). Searching if an item exists in a Set is $O(1)$ (instant), whereas in a List it is $O(N)$ (slow). - Key-Value Mapping (Dict): Perfect for JSON representations and fast lookups based on a specific key (like a user ID).
Python
# English Code Example: Data Structures
# List (Mutable)
users = ["Alice", "Bob"]
users.append("Charlie") # Tuple (Immutable)
coordinates = (10.5, 20.0)
# coordinates[0] = 15.0 # ERROR: 'tuple' object does not support item assignment# Set (Unique values, fast lookup)
emails = {"test@test.com", "admin@test.com", "test@test.com"}
print(emails) # Output: {'test@test.com', 'admin@test.com'} (Duplicate removed)# Dictionary (Key-Value)
user_data = {"id": 1, "name": "Alice"}
print(user_data["name"])
প্রশ্ন ২: Python-এ List, Tuple, Set এবং Dictionary এর মধ্যে মূল পার্থক্যগুলো কী কী?
সংক্ষিপ্ত উত্তর:
- List (
[]): এটি অর্ডারড, মিউটেবল (পরিবর্তনযোগ্য), এবং ডুপ্লিকেট ডেটা সাপোর্ট করে। - Tuple (
()): এটি অর্ডারড, ইমমিউটেবল (তৈরি করার পর আর পরিবর্তন করা যায় না), এবং ডুপ্লিকেট সাপোর্ট করে। এটি মেমরির জন্য খুবই সাশ্রয়ী। - Set (
{}): এটি আন-অর্ডারড, মিউটেবল, কিন্তু কোনো ডুপ্লিকেট ডেটা সাপোর্ট করে না। ডেটা খোঁজার (inঅপারেটর) জন্য এটি অত্যন্ত ফাস্ট। - Dictionary (
{key: value}): এটি কি-ভ্যালু (key-value) পেয়ারের কালেকশন, যেখানে 'কি' গুলোকে অবশ্যই ইউনিক এবং ইমমিউটেবল হতে হয়।
বিস্তারিত উত্তর:
সঠিক ডেটা স্ট্রাকচার নির্বাচন করার ওপর আপনার অ্যাপের পারফরম্যান্স নির্ভর করে।
১. ইমমিউটেবিলিটি (Tuple): ফাংশন থেকে যদি এমন কিছু ডেটা রিটার্ন করেন যা পরিবর্তন হওয়া উচিত নয় (যেমন কো-অর্ডিনেটস), তবে Tuple ব্যবহার করুন। এটি ভুলবশত ডেটা পরিবর্তন হওয়া থেকে বাঁচায়।
২. ইউনিক ডেটা (Set): আপনার কাছে যদি ১০,০০০ ইমেইলের একটি লিস্ট থাকে এবং আপনি ডুপ্লিকেটগুলো মুছতে চান, তবে শুধু set(emails) ব্যবহার করুন। সেট-এর ভেতর কোনো ডেটা আছে কিনা তা খোঁজা $O(1)$ (তাত্ক্ষণিক), যেখানে লিস্টে এটি $O(N)$ (ধীরগতির)।
৩. কি-ভ্যালু (Dict): JSON ডেটা হ্যান্ডল করা এবং নির্দিষ্ট ‘কি’ (যেমন ইউজার আইডি) ধরে দ্রুত ডেটা বের করার জন্য এটি সেরা।
Python
# বাংলা কোড এক্সাম্পল: ডেটা স্ট্রাকচার
# List (পরিবর্তনযোগ্য)
users = ["Alice", "Bob"]
users.append("Charlie") # Tuple (অপরিবর্তনযোগ্য)
coordinates = (10.5, 20.0)
# coordinates[0] = 15.0 # ERROR: তৈরি করার পর পরিবর্তন করা যাবে না# Set (ইউনিক ভ্যালু, দ্রুত সার্চ)
emails = {"test@test.com", "admin@test.com", "test@test.com"}
print(emails) # আউটপুট: {'test@test.com', 'admin@test.com'} (ডুপ্লিকেট রিমুভ হয়ে গেছে)# Dictionary (কি-ভ্যালু)
user_data = {"id": 1, "name": "Alice"}
print(user_data["name"])
Question 3: What is a List Comprehension, and why is it considered more “Pythonic” than standard for loops?
Short Answer:
List Comprehension is a concise, elegant syntax in Python used to create a new list based on the values of an existing iterable (like a list or range) in a single line of code. It is considered “Pythonic” because it replaces multi-line for loops and append() calls with a highly readable, memory-optimized, and computationally faster one-liner.
Detailed Answer:
Once you learn list comprehensions, your Python code becomes much cleaner.
The syntax is: [expression for item in iterable if condition].
- Readability: Instead of reading 4 lines of logic to understand that a list is being filtered and transformed, a list comprehension communicates the entire intent in plain English-like syntax.
- Performance: Under the hood, Python allocates memory differently for list comprehensions. Because the
appendfunction is not being looked up and called repeatedly in each iteration, list comprehensions execute significantly faster than traditional loops in CPython.
Python
# English Code Example: List Comprehension
numbers = [1, 2, 3, 4, 5, 6]# The Traditional Way (Un-pythonic)
even_squares = []
for num in numbers:
if num % 2 == 0:
even_squares.append(num * num)# The Pythonic Way (List Comprehension)
# Read as: "Give me num*num, for every num in numbers, if num is even"
pythonic_squares = [num * num for num in numbers if num % 2 == 0]print(pythonic_squares) # Output: [4, 16, 36]
প্রশ্ন ৩: List Comprehension কী, এবং সাধারণ for লুপের চেয়ে এটিকে কেন বেশি "Pythonic" বলা হয়?
সংক্ষিপ্ত উত্তর:
List Comprehension হলো Python-এর একটি সুন্দর ও সংক্ষিপ্ত সিনট্যাক্স, যা দিয়ে এক লাইনে বিদ্যমান কোনো লিস্ট বা ইটারেবল থেকে শর্তসাপেক্ষে নতুন একটি লিস্ট তৈরি করা যায়। বারবার append() কল করা এবং একাধিক লাইনের for লুপ লেখার ঝামেলা বাঁচিয়ে এটি কোডকে ফাস্ট, রিডেবল (সহজে পড়ার যোগ্য) এবং মেমরি-অপ্টিমাইজড করে, তাই এটিকে "Pythonic" বলা হয়।
বিস্তারিত উত্তর:
List Comprehension শিখলে আপনার কোড অনেক বেশি ক্লিন হয়ে যাবে।
এর সিনট্যাক্স হলো: [expression for item in iterable if condition]।
- রিডেবিলিটি: একটি লিস্ট ফিল্টার করার জন্য ৪ লাইন কোড পড়ার বদলে, লিস্ট কম্প্রিহেনশন দিয়ে মাত্র এক লাইনে সাধারণ ইংরেজির মতো পুরো লজিকটি বুঝিয়ে দেওয়া যায়।
- পারফরম্যান্স: Python ভেতরের আর্কিটেকচারে লিস্ট কম্প্রিহেনশনের জন্য আলাদাভাবে মেমরি বরাদ্দ করে। এখানে প্রতিবার লুপ চলার সময়
appendমেথডটি কল করতে হয় না বলে এটি সাধারণ লুপের চেয়ে অনেক দ্রুত রান করে।
Python
# বাংলা কোড এক্সাম্পল: List Comprehension
numbers = [1, 2, 3, 4, 5, 6]# সাধারণ লুপ (Un-pythonic)
even_squares = []
for num in numbers:
if num % 2 == 0:
even_squares.append(num * num)# পাইথনিক উপায় (List Comprehension)
# পড়ার নিয়ম: "num * num রিটার্ন করো, প্রতিটি num এর জন্য, যদি num জোড় সংখ্যা হয়"
pythonic_squares = [num * num for num in numbers if num % 2 == 0]print(pythonic_squares) # আউটপুট: [4, 16, 36]
Question 4: Explain *args and **kwargs in Python functions. When should you use them?
Short Answer:
*args and **kwargs allow a function to accept a variable (infinite) number of arguments without explicitly defining them in the function signature.
*args(Arguments) collects extra positional arguments into a Tuple.**kwargs(Keyword Arguments) collects extra named (keyword) arguments into a Dictionary.
Detailed Answer:
You use these when building flexible utility functions, wrappers, or decorators where you don’t know in advance how many parameters the user will pass.
- The Unpacking Operator (
*): The magic isn't in the words "args" or "kwargs"; it's in the asterisks (*). The single asterisk packs raw values into a tuple. The double asterisk (**) packs key-value pairs into a dict. You can name them*numbersor**dataif you want. - FastAPI Context: While useful in plain Python, FastAPI actively relies on explicit typing. So while you can use them in FastAPI dependencies, it is usually discouraged for route definitions because FastAPI cannot automatically generate Swagger documentation or validate data for hidden
*args.
Python
# English Code Example: *args and **kwargs
# 1. *args collects values into a Tuple
def sum_all_numbers(*args):
return sum(args)print(sum_all_numbers(1, 2, 3, 4)) # Output: 10# 2. **kwargs collects key=value into a Dictionary
def print_user_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")print_user_info(name="Alice", role="Admin", age=25)
# Output:
# name: Alice
# role: Admin
# age: 25
প্রশ্ন ৪: Python ফাংশনে *args এবং **kwargs কী? এগুলো কখন ব্যবহার করা উচিত?
সংক্ষিপ্ত উত্তর:
ফাংশনে ঠিক কতগুলো আর্গুমেন্ট আসবে তা অজানা থাকলে আনলিমিটেড ভ্যালু রিসিভ করার জন্য *args এবং **kwargs ব্যবহার করা হয়।
*args(Arguments) অতিরিক্ত সাধারণ ভ্যালুগুলোকে একটি Tuple-এ জমা করে।**kwargs(Keyword Arguments) অতিরিক্ত নামসহ (key=value) ভ্যালুগুলোকে একটি Dictionary-তে জমা করে।
বিস্তারিত উত্তর:
যখন আপনি এমন কোনো ইউটিলিটি বা র্যাপার (wrapper) ফাংশন বানান যেখানে ইউজার কয়টি ডেটা পাস করবে তা আপনার জানা নেই, তখন এগুলো ব্যবহার করা হয়।
- আনপ্যাকিং অপারেটর (
*): এখানে "args" বা "kwargs" শব্দগুলোর চেয়ে মূল ম্যাজিক হলো স্টার (*) চিহ্নটি। একটি স্টার সাধারণ ডেটাকে টাপলে এবং ডাবল স্টার (**) কি-ভ্যালু পেয়ারকে ডিকশনারিতে প্যাক করে। আপনি চাইলে এদের নাম*numbersবা**dataও রাখতে পারেন। - FastAPI কনটেক্সট: সাধারণ Python-এ এটি খুব দরকারি হলেও, FastAPI পুরোপুরি সুস্পষ্ট টাইপিং (explicit typing) এর ওপর নির্ভর করে। রাউটের প্যারামিটারে এগুলো ব্যবহার করলে FastAPI স্বয়ংক্রিয়ভাবে Swagger ডক বা ডেটা ভ্যালিডেশন করতে পারে না, তাই FastAPI তে এটি কম ব্যবহৃত হয়।
Python
# বাংলা কোড এক্সাম্পল: *args এবং **kwargs
# ১. *args ভ্যালুগুলোকে Tuple এ প্যাক করে
def sum_all_numbers(*args):
return sum(args)print(sum_all_numbers(1, 2, 3, 4)) # আউটপুট: 10# ২. **kwargs key=value কে Dictionary তে প্যাক করে
def print_user_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")print_user_info(name="Alice", role="Admin", age=25)
# আউটপুট:
# name: Alice
# role: Admin
# age: 25
Question 5: What is a Decorator in Python (@), and why is it absolutely crucial to understand for FastAPI?
Short Answer:
A Decorator is a design pattern in Python that allows you to modify or extend the behavior of a function or class without permanently modifying its actual source code. It takes a function as an argument, wraps it in another function to add functionality (like timing, logging, or routing), and returns the wrapper. In FastAPI, decorators (like @app.get("/")) are fundamentally used to register your standard Python functions as HTTP API endpoints.
Detailed Answer:
Decorators are heavily used in modern Python frameworks.
- How they work: Functions in Python are “first-class citizens”, meaning they can be passed around like variables. A decorator is simply a function that returns another function. The
@symbol is just syntactic sugar formy_function = decorator(my_function). - FastAPI Connection: When you write
@app.get("/users"), you are passing your function to the FastAPIappobject. The decorator secretly registers the URL path, the HTTP method, and the response models into FastAPI's internal router so that when a web request hits/users, FastAPI knows exactly which function to execute.
Python
# English Code Example: Custom Decorator
import time# 1. Defining the decorator
def time_logger(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs) # Execute original function
end = time.time()
print(f"Function {func.__name__} took {end - start} seconds")
return result
return wrapper# 2. Applying the decorator
@time_logger
def slow_function():
time.sleep(1)
print("Task finished")slow_function()
# Output:
# Task finished
# Function slow_function took 1.000... seconds
প্রশ্ন ৫: Python-এ ডেকোরেটর (Decorator — @) কী, এবং FastAPI বোঝার জন্য এটি জানা কেন অত্যন্ত জরুরি?
সংক্ষিপ্ত উত্তর:
ডেকোরেটর হলো Python-এর এমন একটি ডিজাইন প্যাটার্ন যা কোনো ফাংশনের মূল কোড পরিবর্তন না করেই তার কার্যক্ষমতা বাড়িয়ে দেয় বা মডিফাই করে। এটি আর্গুমেন্ট হিসেবে একটি ফাংশন রিসিভ করে, তার সাথে নতুন কিছু লজিক (যেমন- লগিং, রাউটিং) যুক্ত করে এবং নতুন একটি ফাংশন রিটার্ন করে। FastAPI-তে ডেকোরেটর (যেমন @app.get("/")) ব্যবহার করেই সাধারণ Python ফাংশনগুলোকে HTTP API এন্ডপয়েন্ট হিসেবে রেজিস্টার বা যুক্ত করা হয়।
বিস্তারিত উত্তর:
আধুনিক Python ফ্রেমওয়ার্কগুলোতে ডেকোরেটরের প্রচুর ব্যবহার রয়েছে।
- কীভাবে কাজ করে: Python-এ ফাংশন হলো “ফার্স্ট-ক্লাস সিটিজেন”, অর্থাৎ এদেরকে ভেরিয়েবলের মতো পাস করা যায়। ডেকোরেটর মূলত একটি ফাংশন যা আরেকটি ফাংশন রিটার্ন করে।
@চিহ্নটি আসলেmy_function = decorator(my_function)কোডটির একটি শর্টকাট রূপ। - FastAPI-এর সাথে সম্পর্ক: যখন আপনি
@app.get("/users")লেখেন, আপনি মূলত আপনার ফাংশনটিকে FastAPIappঅবজেক্টের কাছে পাস করে দিচ্ছেন। ডেকোরেটরটি ভেতরে ভেতরে URL পাথ এবং HTTP মেথডটি FastAPI-এর রাউটারে সেভ করে রাখে, যাতে ইউজারের রিকোয়েস্ট এলে সে বুঝতে পারে কোন ফাংশনটি রান করতে হবে।
Python
# বাংলা কোড এক্সাম্পল: কাস্টম ডেকোরেটর তৈরি
import time# ১. ডেকোরেটর ডিফাইন করা
def time_logger(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs) # মূল ফাংশনটি এক্সিকিউট করা
end = time.time()
print(f"Function {func.__name__} took {end - start} seconds")
return result
return wrapper# ২. ডেকোরেটর ব্যবহার করা
@time_logger
def slow_function():
time.sleep(1)
print("Task finished")slow_function()
# আউটপুট:
# Task finished
# Function slow_function took 1.000... seconds
Question 6: What are Type Hints in Python, and how does FastAPI leverage them?
Short Answer:
Type Hints (introduced in Python 3.5) allow developers to statically declare the expected data types of variables, function parameters, and return values (e.g., def get_user(user_id: int) -> str:). While standard Python ignores these hints at runtime, FastAPI leverages them extensively using the Pydantic library to automatically perform runtime data validation, data conversion, and generate interactive Swagger API documentation.
Detailed Answer:
Type hints shifted Python from a purely dynamic language to a highly predictable one.
- Standard Python: If you write
def add(a: int, b: int):, Python will still let you passadd("hello", "world")without crashing. It's just a "hint" for your IDE (like VSCode) to catch errors during development. - The FastAPI Magic: FastAPI is one of the few frameworks that reads these Type Hints during execution. If you define a FastAPI route with
user_id: int, and a client sendsGET /users/abc, FastAPI reads theinttype hint, intercepts the request, and instantly returns a422 Unprocessable Entityerror automatically. You don't have to write a single line of validation code.
Python
# English Code Example: Type Hints in FastAPI
from fastapi import FastAPIapp = FastAPI()# By explicitly typing item_id as 'int' and q as 'str' (or None),
# FastAPI automatically handles validation and IDE auto-completion.
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None) -> dict:
# If the user sends /items/apple, FastAPI throws a 422 Error automatically
return {"item_id": item_id, "query": q}
প্রশ্ন ৬: Python-এ Type Hints কী, এবং FastAPI কীভাবে এর সুবিধা গ্রহণ করে?
সংক্ষিপ্ত উত্তর:
Type Hints (Python ৩.৫ থেকে আসা) ব্যবহার করে ডেভেলপাররা ভেরিয়েবল, ফাংশন প্যারামিটার এবং রিটার্ন ভ্যালুর ডেটা টাইপ নির্দিষ্ট করে দিতে পারে (যেমন def get_user(user_id: int) -> str:)। সাধারণ Python রানটাইমে এই হিন্টসগুলো ইগনোর করলেও, FastAPI এগুলোকে ব্যাপকভাবে ব্যবহার করে। Pydantic লাইব্রেরির মাধ্যমে FastAPI এই টাইপ হিন্টস দেখেই স্বয়ংক্রিয়ভাবে ডেটা ভ্যালিডেশন করে, ডেটা কনভার্ট করে এবং Swagger API ডকুমেন্টেশন তৈরি করে।
বিস্তারিত উত্তর:
Type Hints আসার পর Python অনেক বেশি নির্ভরযোগ্য ভাষায় পরিণত হয়েছে।
- সাধারণ Python: আপনি যদি
def add(a: int, b: int):লেখেন, তবুও Python আপনাকেadd("hello", "world")রান করতে দেবে। এটি মূলত আপনার কোড এডিটরকে (VSCode) ডেভেলপমেন্টের সময় এরর ধরার সুযোগ করে দেওয়ার একটি "হিন্ট" বা ইশারা মাত্র। - FastAPI ম্যাজিক: FastAPI সেই হাতেগোনা কয়েকটি ফ্রেমওয়ার্কের একটি, যা রানটাইমে এই Type Hints গুলো পড়ে। আপনি যদি রাউটে
user_id: intলেখেন এবং কোনো ক্লায়েন্টGET /users/abcরিকোয়েস্ট পাঠায়, FastAPI ওইintদেখে রিকোয়েস্টটি আটকে দেবে এবং নিজে থেকেই422 Unprocessable Entityএরর রিটার্ন করবে। আপনাকে ম্যানুয়ালি কোনো ভ্যালিডেশন কোড লিখতে হবে না।
Python
# বাংলা কোড এক্সাম্পল: FastAPI তে Type Hints এর ব্যবহার
from fastapi import FastAPIapp = FastAPI()# item_id কে 'int' এবং q কে 'str' বা None হিসেবে টাইপ করায়,
# FastAPI নিজে থেকেই ভ্যালিডেশন এবং অটো-কমপ্লিশন হ্যান্ডল করবে।
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str | None = None) -> dict:
# ইউজার যদি /items/apple পাঠায়, FastAPI নিজে থেকেই 422 Error দেবে
return {"item_id": item_id, "query": q}
Question 7: Synchronous vs. Asynchronous execution in Python (async/await). Why is it important?
Short Answer:
Synchronous execution runs code line-by-line, blocking the entire program while waiting for slow operations (like a database query or external API call) to finish. Asynchronous execution (async / await via the asyncio library) allows the Python Event Loop to pause the current function at the await keyword during a slow I/O operation, switching to execute other incoming requests simultaneously, resulting in drastically higher scalability.
Detailed Answer:
For web development, understanding async is the key to building fast APIs.
- The Synchronous Bottleneck: If your standard Flask/Django app receives 10 requests, and each requires a 1-second database query, the 10th user must wait 10 seconds just to get a response.
- The Asynchronous Solution: By defining a route with
async defand usingawait db.query(), Python says: "This database call will take 1 second. Instead of freezing the server, I'll pause this function and go handle the 2nd, 3rd, and 10th user requests." When the database finally responds, Python resumes the first function. This allows a single Python thread to handle thousands of concurrent connections (concurrency, not parallelism).
Python
# English Code Example: Async/Await concept
import asyncioasync def fetch_data_from_db():
print("1. DB query started...")
# Simulates a slow 2-second database call without blocking the whole server
await asyncio.sleep(2)
print("3. DB query finished!")
return {"data": "Success"}async def handle_request():
# Await tells the event loop it can do other work while waiting here
result = await fetch_data_from_db()
print("4. Returning result to user:", result)async def other_task():
print("2. Server handling other users in the background...")async def main():
# Running multiple tasks concurrently
await asyncio.gather(handle_request(), other_task())asyncio.run(main())
প্রশ্ন ৭: Python-এ Synchronous এবং Asynchronous (async/await) এক্সিকিউশনের মধ্যে পার্থক্য কী? এটি কেন গুরুত্বপূর্ণ?
সংক্ষিপ্ত উত্তর:
Synchronous এক্সিকিউশন কোডকে লাইন-বাই-লাইন রান করে, ফলে কোনো ধীরগতির কাজ (যেমন ডেটাবেস কল বা এক্সটার্নাল API) শেষ না হওয়া পর্যন্ত পুরো প্রোগ্রামটি আটকে (block) থাকে। অন্যদিকে Asynchronous এক্সিকিউশন (asyncio লাইব্রেরি এবং async/await এর মাধ্যমে) Python Event Loop-কে সুযোগ দেয় ধীরগতির কাজের সময় await কি-ওয়ার্ডে বর্তমান ফাংশনটিকে সাময়িক পজ (pause) করে অন্য রিকোয়েস্টগুলো প্রসেস করার। এর ফলে অ্যাপের স্কেলেবিলিটি বহুগুণ বেড়ে যায়।
বিস্তারিত উত্তর:
ওয়েব ডেভেলপমেন্টে ফাস্ট API বানানোর মূল চাবিকাঠি হলো async বোঝা।
- Synchronous সীমাবদ্ধতা: আপনার সাধারণ Flask বা Django অ্যাপে যদি ১০টি রিকোয়েস্ট আসে এবং প্রতিটিতে ১ সেকেন্ডের ডেটাবেস কুয়েরি থাকে, তবে ১০ নম্বর ইউজারকে রেসপন্স পাওয়ার জন্য ১০ সেকেন্ড অপেক্ষা করতে হবে।
- Asynchronous সমাধান:
async defদিয়ে রাউট তৈরি করেawait db.query()ব্যবহার করলে Python বলে: "এই ডেটাবেস কলে ১ সেকেন্ড লাগবে। আমি সার্ভার ব্লক না করে এই ফাংশনটি এখানে পজ করে ২য়, ৩য় বা ১০ম ইউজারের রিকোয়েস্ট রিসিভ করতে যাই।" ডেটাবেস থেকে ডেটা আসলে Python আবার প্রথম ফাংশনে ফিরে আসে। এর ফলে একটিমাত্র থ্রেড দিয়ে হাজার হাজার ইউজার হ্যান্ডল করা যায়।
Python
# বাংলা কোড এক্সাম্পল: Async/Await এর কনসেপ্ট
import asyncioasync def fetch_data_from_db():
print("১. ডেটাবেস কুয়েরি শুরু হলো...")
# পুরো সার্ভার ব্লক না করে ২ সেকেন্ডের একটি ধীরগতির ডেটাবেস কল সিমুলেট করা
await asyncio.sleep(2)
print("৩. ডেটাবেস কুয়েরি শেষ হলো!")
return {"data": "Success"}async def handle_request():
# Await ইভেন্ট লুপকে জানিয়ে দেয় যে সে অপেক্ষার সময় অন্য কাজ করতে পারবে
result = await fetch_data_from_db()
print("৪. ইউজারকে রেজাল্ট রিটার্ন করা হচ্ছে:", result)async def other_task():
print("২. সার্ভার ব্যাকগ্রাউন্ডে অন্য ইউজারদের হ্যান্ডল করছে...")async def main():
# একাধিক কাজ একসাথে (কনকারেন্টলি) রান করা
await asyncio.gather(handle_request(), other_task())asyncio.run(main())
Question 8: What is FastAPI, and what makes it structurally faster and more modern than older frameworks like Flask or Django?
Short Answer:
FastAPI is a modern, fast (high-performance) web framework for building APIs with Python. Structurally, it does not reinvent the wheel; it is built on top of two extremely fast underlying libraries: Starlette (for asynchronous web routing and HTTP handling) and Pydantic (for data validation and serialization). This combination makes FastAPI natively asynchronous, inherently type-safe, and capable of automatically generating interactive Swagger documentation.
Detailed Answer:
For 6 months of experience, it’s crucial to understand why FastAPI is so popular right now.
- Asynchronous by Default (Starlette): Django and Flask were built years ago for synchronous Python. FastAPI was built from day one to fully support
async/await, matching the speed of NodeJS and Go. - Data Validation (Pydantic): In Flask, you have to manually parse
request.jsonand writeif type(age) != int:checks. FastAPI delegates this entirely to Pydantic, ensuring that invalid data never even reaches your route logic. - Developer Experience (DX): Because of Python Type Hints, FastAPI auto-generates OpenAPI (Swagger) documentation (
/docs) in real-time, eliminating the need to write and maintain manual API documentation.
প্রশ্ন ৮: FastAPI কী, এবং কোন কারণে এটি Flask বা Django-এর মতো পুরনো ফ্রেমওয়ার্কগুলোর চেয়ে আর্কিটেকচারালি ফাস্ট এবং আধুনিক?
সংক্ষিপ্ত উত্তর:
FastAPI হলো Python দিয়ে API বানানোর একটি আধুনিক এবং অত্যন্ত ফাস্ট ওয়েব ফ্রেমওয়ার্ক। আর্কিটেকচারালি এটি নিজে সবকিছু নতুন করে বানায়নি; এটি দুটি সুপারফাস্ট লাইব্রেরির ওপর দাঁড়িয়ে আছে: Starlette (অ্যাসিনক্রোনাস ওয়েব রাউটিংয়ের জন্য) এবং Pydantic (ডেটা ভ্যালিডেশন এবং টাইপিংয়ের জন্য)। এই কম্বিনেশনের কারণে FastAPI জন্মগতভাবেই অ্যাসিনক্রোনাস, টাইপ-সেফ এবং নিজে থেকেই Swagger ডকুমেন্টেশন তৈরি করতে পারে।
বিস্তারিত উত্তর:
৬ মাসের অভিজ্ঞতার ডেভেলপার হিসেবে আপনার বোঝা উচিত কেন FastAPI এত জনপ্রিয়।
১. ডিফল্টভাবে অ্যাসিনক্রোনাস (Starlette): Django এবং Flask তৈরি হয়েছিল অনেক আগে, যখন Python সিনক্রোনাস ছিল। কিন্তু FastAPI প্রথম দিন থেকেই async/await সাপোর্ট করার জন্য তৈরি, যার কারণে এর স্পিড NodeJS বা Go-এর সমতুল্য।
২. ডেটা ভ্যালিডেশন (Pydantic): Flask-এ আপনাকে ম্যানুয়ালি request.json পার্স করে if type(age) != int: চেক লিখতে হয়। FastAPI এই কাজ পুরোপুরি Pydantic-কে দিয়ে দেয়, ফলে ভুল বা ইনভ্যালিড ডেটা আপনার রাউট লজিক পর্যন্ত পৌঁছাতেই পারে না।
৩. ডেভেলপার এক্সপেরিয়েন্স (DX): Type Hints ব্যবহার করার কারণে FastAPI নিজে থেকেই রিয়েল-টাইমে OpenAPI (Swagger) ডকুমেন্টেশন (/docs) তৈরি করে ফেলে, তাই কষ্ট করে আর API ডকস লিখতে বা আপডেট করতে হয় না।
Question 9: What are Pydantic Models in FastAPI, and what dual purpose do they serve?
Short Answer:
Pydantic is a data validation library that uses Python classes with Type Hints. In FastAPI, a Pydantic Model (a class inheriting from BaseModel) serves a dual purpose: Data Validation (verifying that the incoming JSON payload exactly matches the expected types and constraints) and Data Serialization/Deserialization (automatically converting JSON strings into Python objects, and Python objects back into JSON responses).
Detailed Answer:
Pydantic is the true engine behind FastAPI’s developer experience.
- Without Pydantic: You receive a raw JSON dictionary. You have to check if the key “price” exists, check if it’s a number, and manually throw an error if it’s negative.
- With Pydantic: You define a class. Pydantic handles the parsing. If a user sends
"price": "100"(a string), Pydantic is smart enough to coerce (convert) it into the integer100. If they send"price": "abc", Pydantic throws a clean, detailed error specifying exactly which field failed and why.
Python
# English Code Example: Pydantic Model
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()# 1. Define the Schema (Pydantic Model)
class Product(BaseModel):
name: str
price: float
is_active: bool = True # Optional with a default value# 2. Use the Model in the Route
@app.post("/products/")
async def create_product(product: Product):
# 'product' is now a fully validated Python object, not a raw dict.
# You get full auto-complete: product.name, product.price
return {"message": "Product created", "data": product}
প্রশ্ন ৯: FastAPI-তে Pydantic Models কী, এবং এরা কোন দুটি মূল কাজ সম্পন্ন করে?
সংক্ষিপ্ত উত্তর:
Pydantic হলো একটি ডেটা ভ্যালিডেশন লাইব্রেরি যা Type Hints সম্বলিত Python ক্লাস ব্যবহার করে। FastAPI-তে একটি Pydantic Model (যা BaseModel থেকে ইনহেরিট করে তৈরি) দুটি প্রধান কাজ করে: Data Validation (নিশ্চিত করে যে আসা JSON ডেটা সঠিক টাইপের এবং শর্ত পূরণ করছে) এবং Serialization/Deserialization (স্বয়ংক্রিয়ভাবে JSON-কে Python অবজেক্টে এবং Python অবজেক্টকে JSON রেসপন্সে রূপান্তর করে)।
বিস্তারিত উত্তর:
FastAPI-এর দারুণ ডেভেলপার এক্সপেরিয়েন্সের মূল কারিগরই হলো Pydantic।
- Pydantic ছাড়া: আপনি একটি র (raw) JSON ডিকশনারি পান। আপনাকে নিজে চেক করতে হয় “price” নামে কিছু আছে কিনা, সেটি নাম্বার কিনা, এবং মাইনাস হলে নিজে থেকে এরর থ্রো করতে হয়।
- Pydantic সহ: আপনি একটি ক্লাস ডিফাইন করেন। Pydantic সব চেকিংয়ের দায়িত্ব নেয়। ইউজার যদি
"price": "100"(স্ট্রিং হিসেবে) পাঠায়, Pydantic স্মার্টলি সেটিকে ইন্টিজার100-এ কনভার্ট করে নেবে। আর যদি"price": "abc"পাঠায়, তবে সে একটি সুন্দর ও বিস্তারিত এরর মেসেজ দিয়ে বলে দেবে ঠিক কোন ফিল্ডে কেন এরর হয়েছে।
Python
# বাংলা কোড এক্সাম্পল: Pydantic মডেলের ব্যবহার
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()# ১. স্কিমা বা Pydantic মডেল তৈরি করা
class Product(BaseModel):
name: str
price: float
is_active: bool = True # ডিফল্ট ভ্যালুসহ এটি অপশনাল# ২. রাউটে মডেলটি ব্যবহার করা
@app.post("/products/")
async def create_product(product: Product):
# 'product' এখন একটি শতভাগ ভ্যালিড Python অবজেক্ট, সাধারণ ডিকশনারি নয়।
# আপনি কোড এডিটরে অটো-কমপ্লিট পাবেন: product.name, product.price
return {"message": "প্রোডাক্ট তৈরি হয়েছে", "data": product}
Question 10: Path Parameters vs. Query Parameters in FastAPI. How do you define them and when should you use which?
Short Answer:
- Path Parameters: Variables directly embedded in the URL path (e.g.,
/users/5). They are mandatory and used to identify a specific resource. Defined in the decorator path like@app.get("/users/{user_id}"). - Query Parameters: Key-value pairs added to the end of a URL after a
?(e.g.,/users?limit=10). They are usually optional and used to filter, sort, or paginate a list of resources. In FastAPI, any function parameter that is not in the decorator path is automatically treated as a query parameter.
Detailed Answer:
Designing clean REST APIs requires knowing when to use Path vs. Query parameters.
- Usage Rule: If fetching a specific unique book, use Path Parameter:
GET /books/{id}. If searching for books by a specific author or limiting the results, use Query Parameters:GET /books?author=John&limit=20. - FastAPI Magic: FastAPI natively recognizes both. If you assign a default value to a parameter (like
skip: int = 0), FastAPI automatically makes it an optional Query Parameter.
Python
# English Code Example: Path vs Query Parameters
from fastapi import FastAPIapp = FastAPI()# "user_id" is a Path Parameter because it is inside the {brackets}
# "skip" and "limit" are Query Parameters because they are NOT in the brackets
@app.get("/users/{user_id}")
async def get_user_details(user_id: int, skip: int = 0, limit: int = 10):
return {
"target_user_id": user_id, # Required from path
"pagination_skip": skip, # Optional from query (default 0)
"pagination_limit": limit # Optional from query (default 10)
}# Example Request: GET /users/42?skip=5&limit=20
প্রশ্ন ১০: FastAPI-তে Path Parameters এবং Query Parameters কী? কীভাবে এগুলো ডিফাইন করবেন এবং কখন কোনটি ব্যবহার করবেন?
সংক্ষিপ্ত উত্তর:
- Path Parameters: এগুলো সরাসরি URL পাথের ভেতরে থাকে (যেমন
/users/5)। এগুলো বাধ্যতামূলক এবং কোনো নির্দিষ্ট ডেটাকে পয়েন্ট করতে ব্যবহৃত হয়। ডেকোরেটরের পাথে{}দিয়ে (যেমন@app.get("/users/{user_id}")) এগুলো ডিফাইন করতে হয়। - Query Parameters: এগুলো URL-এর শেষে
?চিহ্নের পর কি-ভ্যালু (key-value) পেয়ার হিসেবে থাকে (যেমন/users?limit=10)। এগুলো সাধারণত অপশনাল এবং ডেটার লিস্ট ফিল্টার, সর্ট বা পেজিনেট করতে ব্যবহৃত হয়। FastAPI-তে ফাংশনের যেসব প্যারামিটার ডেকোরেটরের পাথে থাকে না, সেগুলো নিজে থেকেই Query Parameter হিসেবে কাজ করে।
বিস্তারিত উত্তর:
সুন্দর REST API বানানোর জন্য Path এবং Query প্যারামিটারের সঠিক ব্যবহার জানা জরুরি।
- ব্যবহারের নিয়ম: কোনো নির্দিষ্ট বা ইউনিক বই খুঁজতে Path Parameter ব্যবহার করুন:
GET /books/{id}। কিন্তু কোনো নির্দিষ্ট রাইটারের বই খুঁজতে বা ডেটার লিমিট দিতে Query Parameter ব্যবহার করুন:GET /books?author=John&limit=20। - FastAPI ম্যাজিক: FastAPI দুটোই নিজে থেকে চিনে নেয়। আপনি যদি প্যারামিটারে কোনো ডিফল্ট ভ্যালু দেন (যেমন
skip: int = 0), তবে FastAPI বুঝে যায় যে এটি একটি অপশনাল Query Parameter।
Python
# বাংলা কোড এক্সাম্পল: Path বনাম Query Parameters
from fastapi import FastAPIapp = FastAPI()# "user_id" হলো Path Parameter কারণ এটি {ব্র্যাকেট}-এর ভেতরে আছে
# "skip" এবং "limit" হলো Query Parameters কারণ এরা ব্র্যাকেটে নেই
@app.get("/users/{user_id}")
async def get_user_details(user_id: int, skip: int = 0, limit: int = 10):
return {
"target_user_id": user_id, # পাথের জন্য বাধ্যতামূলক
"pagination_skip": skip, # কুয়েরির জন্য অপশনাল (ডিফল্ট 0)
"pagination_limit": limit # কুয়েরির জন্য অপশনাল (ডিফল্ট 10)
}# এক্সাম্পল রিকোয়েস্ট: GET /users/42?skip=5&limit=20
(Phase 1 শেষ হলো। আপনি যখন প্রস্তুত হবেন, আমাকে জানাবেন, আমি Phase 2 জেনারেট করব, যেখানে Database, SQLAlchemy, Dependency Injection এবং আরও অ্যাডভান্সড FastAPI টপিক থাকবে!)
আরে হ্যাঁ, আমি অত্যন্ত দুঃখিত! আগের মেসেজে একটু ভুল বোঝাবুঝি হয়ে গিয়েছিল। আপনার Python এবং FastAPI এর Phase 2 একদম প্রস্তুত।
যেহেতু আপনার ৬ মাসের অভিজ্ঞতা আছে, এই পর্বে আমরা FastAPI-এর কিছু অত্যন্ত শক্তিশালী কোর কনসেপ্ট — যেমন Dependency Injection, Database Integration (SQLAlchemy), APIRouter এবং Background Tasks নিয়ে আলোচনা করব (প্রশ্ন ১১ থেকে ২০)।
Phase 2: FastAPI Architecture, Dependency Injection & Database (6-Month Experience Level)
Question 11: What is Dependency Injection (DI) in FastAPI (Depends()), and why is it so powerful?
Short Answer:
Dependency Injection (DI) is a design pattern where an object or function receives other objects or functions it depends on, rather than creating them internally. In FastAPI, Depends() is used to inject dependencies (like database sessions, authentication checks, or query parameters) directly into route functions. It promotes code reusability, simplifies testing, and keeps route handlers clean.
Detailed Answer:
FastAPI has one of the best DI systems in the Python ecosystem.
- Without DI: If 20 routes need a database connection, you might have to write
db = get_db_connection()inside every single function. If you need to verify a user's token, you write token verification logic inside every route. - With DI (
Depends): You write a separate functionverify_token(). Then in your route, you simply writeuser = Depends(verify_token). FastAPI automatically executesverify_tokenbefore your route runs, handles any errors (like throwing a 401 if the token is invalid), and passes the result directly into your route. It makes testing easy because you can override dependencies with mock data during unit tests.
Python
# English Code Example: Dependency Injection
from fastapi import FastAPI, Depends, HTTPExceptionapp = FastAPI()# 1. The Dependency Function
def verify_token(token: str):
if token != "supersecret":
raise HTTPException(status_code=401, detail="Invalid token")
return {"user": "admin"}# 2. Injecting the Dependency
@app.get("/dashboard")
async def get_dashboard(user_data: dict = Depends(verify_token)):
# This code ONLY runs if verify_token succeeds
return {"message": "Welcome to dashboard", "user": user_data["user"]}
প্রশ্ন ১১: FastAPI-তে Dependency Injection (DI) বা Depends() কী, এবং এটি এত শক্তিশালী কেন?
সংক্ষিপ্ত উত্তর:
Dependency Injection (DI) হলো এমন একটি ডিজাইন প্যাটার্ন যেখানে কোনো ফাংশন বা অবজেক্ট তার প্রয়োজনীয় অন্যান্য ফাংশন নিজে তৈরি না করে, বাইরে থেকে রিসিভ করে। FastAPI-তে Depends() ব্যবহার করে রাউট ফাংশনের ভেতর সরাসরি ডিপেন্ডেন্সি (যেমন- ডেটাবেস সেশন, অথেনটিকেশন চেক বা কুয়েরি প্যারামিটার) ইনজেক্ট করা হয়। এটি কোডের পুনরাবৃত্তি কমায়, টেস্টিং সহজ করে এবং রাউটগুলোকে পরিষ্কার রাখে।
বিস্তারিত উত্তর:
FastAPI-এর DI সিস্টেমটি Python ইকোসিস্টেমের অন্যতম সেরা।
- DI ছাড়া: ২০টি রাউটে ডেটাবেস লাগলে, সবগুলোর ভেতরে আপনাকে
db = get_db_connection()লিখতে হবে। টোকেন ভেরিফাই করতে হলে সব রাউটে একই লজিক বারবার লিখতে হবে। - DI সহ (
Depends): আপনিverify_token()নামে আলাদা একটি ফাংশন লিখবেন। এরপর রাউটের প্যারামিটারে শুধুuser = Depends(verify_token)লিখে দেবেন। রিকোয়েস্ট এলে FastAPI রাউট রান করার আগেই টোকেন ভেরিফাই করবে। টোকেন ভুল হলে সে নিজে থেকেই 401 এরর দেবে। আর ঠিক থাকলে ইউজারের ডেটা রাউটে পাস করে দেবে। ইউনিট টেস্ট করার সময় ফেক ডেটা দিয়ে ডিপেন্ডেন্সি ওভাররাইড করাও খুব সহজ।
Python
# বাংলা কোড এক্সাম্পল: Dependency Injection
from fastapi import FastAPI, Depends, HTTPExceptionapp = FastAPI()# ১. ডিপেন্ডেন্সি ফাংশন
def verify_token(token: str):
if token != "supersecret":
raise HTTPException(status_code=401, detail="ইনভ্যালিড টোকেন")
return {"user": "admin"}# ২. ডিপেন্ডেন্সি ইনজেক্ট করা
@app.get("/dashboard")
async def get_dashboard(user_data: dict = Depends(verify_token)):
# এই কোডটি কেবল তখনই রান করবে যদি verify_token সফল হয়
return {"message": "ড্যাশবোর্ডে স্বাগতম", "user": user_data["user"]}
Question 12: How do you structure a growing FastAPI application? Explain the role of APIRouter.
Short Answer:
As a FastAPI application grows, keeping all routes in a single main.py file becomes unmaintainable. We structure the app modularly using APIRouter. APIRouter acts as a "mini FastAPI app" that groups related routes together (e.g., all user routes in routers/users.py). These separate routers are then imported and mounted onto the main FastAPI app using app.include_router().
Detailed Answer:
Structuring your app properly is a sign of an intermediate/senior developer.
- The Architecture: Usually, we create a folder named
routers/orapi/. Inside, we have files likeusers.pyandproducts.py. - Using APIRouter: Inside
users.py, instead ofapp = FastAPI(), we writerouter = APIRouter(prefix="/users", tags=["Users"]). This allows us to define routes like@router.get("/")which automatically maps to/users/. - Mounting: In the core
main.pyfile, we just import these routers and tie them together, keeping the main entry point incredibly clean.
Python
# English Code Example: APIRouter
# --- file: routers/users.py ---
from fastapi import APIRouter# Create the router with a prefix
router = APIRouter(prefix="/users", tags=["User Management"])@router.get("/")
async def get_users():
return [{"name": "Alice"}] # Actually maps to GET /users/# --- file: main.py ---
from fastapi import FastAPI
from routers import users # Import the routerapp = FastAPI()# Mount the router to the main app
app.include_router(users.router)
প্রশ্ন ১২: একটি বড় FastAPI অ্যাপ্লিকেশনকে আপনি কীভাবে স্ট্রাকচার (structure) করেন? APIRouter এর ভূমিকা বুঝিয়ে বলুন।
সংক্ষিপ্ত উত্তর:
FastAPI অ্যাপ্লিকেশন বড় হতে থাকলে সব রাউট একটি main.py ফাইলে রাখা অসম্ভব হয়ে পড়ে। তখন আমরা APIRouter ব্যবহার করে অ্যাপটিকে মডুলারভাবে সাজাই। APIRouter একটি "মিনি FastAPI অ্যাপ" হিসেবে কাজ করে যা সম্পর্কিত রাউটগুলোকে একসাথে গ্রুপ করে (যেমন routers/users.py-তে সব ইউজার রাউট)। এরপর এই রাউটারগুলোকে ইমপোর্ট করে মূল app-এর সাথে app.include_router() দিয়ে যুক্ত করে দেওয়া হয়।
বিস্তারিত উত্তর:
অ্যাপ্লিকেশন সঠিকভাবে স্ট্রাকচার করা একজন অভিজ্ঞ ডেভেলপারের লক্ষণ।
- আর্কিটেকচার: সাধারণত আমরা
routers/বাapi/নামে একটি ফোল্ডার বানাই। এর ভেতরusers.py,products.pyইত্যাদি ফাইল থাকে। - APIRouter এর ব্যবহার:
users.pyএর ভেতরapp = FastAPI()লেখার বদলে আমরা লিখিrouter = APIRouter(prefix="/users", tags=["Users"])। এর ফলে@router.get("/")লিখলে তা স্বয়ংক্রিয়ভাবে/users/পাথে ম্যাপ হয়ে যায়। - যুক্ত করা (Mounting): মূল
main.pyফাইলে আমরা শুধু এই রাউটারগুলো ইমপোর্ট করে যুক্ত করে দিই। এতেmain.pyফাইলটি একদম ক্লিন থাকে।
Python
# বাংলা কোড এক্সাম্পল: APIRouter এর ব্যবহার
# --- ফাইল: routers/users.py ---
from fastapi import APIRouter# প্রিফিক্সসহ রাউটার তৈরি করা
router = APIRouter(prefix="/users", tags=["User Management"])@router.get("/")
async def get_users():
return [{"name": "Alice"}] # এটি মূলত GET /users/ এ কাজ করবে# --- ফাইল: main.py ---
from fastapi import FastAPI
from routers import users # রাউটার ইমপোর্ট করাapp = FastAPI()# মূল অ্যাপের সাথে রাউটার যুক্ত করা
app.include_router(users.router)
Question 13: What is CORS (Cross-Origin Resource Sharing), and how do you configure it in FastAPI?
Short Answer:
CORS is a security feature enforced by web browsers that prevents a frontend web application running on one domain (e.g., http://localhost:3000) from making API requests to a backend server running on a different domain (e.g., http://localhost:8000). To allow frontend apps to talk to your FastAPI backend, you must configure CORS middleware using CORSMiddleware and explicitly define which origins (domains) are allowed to access your API.
Detailed Answer:
Every developer faces CORS errors when they connect their React/Vue frontend to their backend for the first time.
- The Browser Block: It’s important to know that APIs don’t block CORS; browsers do. If you test your API with Postman, it works perfectly. But a browser checks the backend’s headers. If the backend doesn’t explicitly say “I allow requests from localhost:3000”, the browser throws a CORS error.
- FastAPI Setup: FastAPI provides built-in middleware for this. You add it to your
appand provide a list ofallow_origins. In production, you should never use["*"](which allows any website to access your API) as it is a major security vulnerability.
Python
# English Code Example: CORS Configuration
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddlewareapp = FastAPI()# Explicitly whitelist your frontend domains
origins = [
"http://localhost:3000", # React local dev
"https://myfrontendapp.com" # Production frontend
]app.add_middleware(
CORSMiddleware,
allow_origins=origins, # Domains allowed to connect
allow_credentials=True, # Allow cookies/authorization headers
allow_methods=["*"], # Allow all HTTP methods (GET, POST, etc.)
allow_headers=["*"], # Allow all headers
)
প্রশ্ন ১৩: CORS (Cross-Origin Resource Sharing) কী, এবং FastAPI-তে আপনি এটি কীভাবে কনফিগার করবেন?
সংক্ষিপ্ত উত্তর:
CORS হলো ওয়েব ব্রাউজারের একটি সিকিউরিটি ফিচার, যা এক ডোমেইনে চলা ফ্রন্টএন্ড অ্যাপকে (যেমন http://localhost:3000) অন্য ডোমেইনে চলা ব্যাকএন্ড সার্ভারে (যেমন http://localhost:8000) রিকোয়েস্ট পাঠাতে বাধা দেয়। আপনার FastAPI ব্যাকএন্ডের সাথে ফ্রন্টএন্ডকে কানেক্ট করতে চাইলে CORSMiddleware ব্যবহার করে আপনাকে বলে দিতে হবে ঠিক কোন কোন ডোমেইন (origins) আপনার API অ্যাক্সেস করতে পারবে।
বিস্তারিত উত্তর:
ফ্রন্টএন্ডের (React/Vue) সাথে ব্যাকএন্ড কানেক্ট করার সময় সব ডেভেলপারই এই CORS এররের সম্মুখীন হন।
- ব্রাউজারের বাধা: মনে রাখবেন, API কখনো CORS ব্লক করে না, ব্লক করে ব্রাউজার। আপনি Postman দিয়ে টেস্ট করলে API ঠিকই কাজ করবে। কিন্তু ব্রাউজার রিকোয়েস্ট পাঠানোর আগে ব্যাকএন্ডের হেডার চেক করে। ব্যাকএন্ড যদি বলে না দেয় যে “আমি localhost:3000 কে চিনি”, তবে ব্রাউজার এরর দিয়ে রিকোয়েস্ট বাতিল করবে।
- FastAPI সেটআপ: FastAPI-তে এর জন্য বিল্ট-ইন মিডলওয়্যার আছে। প্রোডাকশনে কখনোই
["*"](সবাইকে পারমিশন দেওয়া) ব্যবহার করা উচিত নয়, কারণ এটি বড় ধরনের সিকিউরিটি ঝুঁকি তৈরি করে।
Python
# বাংলা কোড এক্সাম্পল: CORS কনফিগারেশন
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddlewareapp = FastAPI()# আপনার ফ্রন্টএন্ড ডোমেইনগুলোকে হোয়াইটলিস্ট (whitelist) করা
origins = [
"http://localhost:3000", # React লোকাল ডেভেলপমেন্ট
"https://myfrontendapp.com" # প্রোডাকশন ফ্রন্টএন্ড
]app.add_middleware(
CORSMiddleware,
allow_origins=origins, # যেসব ডোমেইন কানেক্ট করতে পারবে
allow_credentials=True, # কুকি বা অথরাইজেশন হেডার পারমিশন
allow_methods=["*"], # সব HTTP মেথড (GET, POST) অ্যালাউ করা
allow_headers=["*"], # সব হেডার অ্যালাউ করা
)
Question 14: Explain the difference between FastAPI’s BackgroundTasks and using an external tool like Celery.
Short Answer:
FastAPI’s built-in BackgroundTasks allow you to run simple, lightweight tasks (like sending a welcome email) asynchronously in the same event loop after returning a response to the user. It requires zero external setup. Celery (usually paired with Redis/RabbitMQ) is a heavy-duty, distributed task queue. It is required for CPU-intensive or long-running tasks (like video processing) that would otherwise block the FastAPI server, and it provides advanced features like task retries, scheduling, and scaling across multiple worker servers.
Detailed Answer:
BackgroundTasks: It is a class imported fromfastapi. You inject it into your route, add a function to it, and FastAPI executes it in the background right after sending the HTTP response. If the FastAPI server crashes during the task, the task is lost forever.- Celery: If a task takes 5 minutes to run, you cannot use
BackgroundTasksbecause it will consume your server's RAM and thread resources. Celery shifts the workload completely outside of FastAPI to a separate worker process. If the worker crashes, the queue remembers the task and retries it later.
Python
# English Code Example: FastAPI BackgroundTasks
from fastapi import FastAPI, BackgroundTasks
import timeapp = FastAPI()# A simple task function
def send_email_notification(email: str):
time.sleep(3) # Simulating a 3-second delay
print(f"Email sent to {email}")@app.post("/register")
async def register_user(email: str, bg_tasks: BackgroundTasks):
# 1. Add the task to be run in the background
bg_tasks.add_task(send_email_notification, email)
# 2. Return the response INSTANTLY without waiting for the 3-second email delay
return {"message": "User registered. Email will be sent shortly."}
প্রশ্ন ১৪: FastAPI-এর নিজস্ব BackgroundTasks এবং Celery-এর মতো এক্সটার্নাল টুলের মধ্যে পার্থক্য কী?
সংক্ষিপ্ত উত্তর:
FastAPI-এর বিল্ট-ইন BackgroundTasks ব্যবহার করে ইউজারকে রেসপন্স দেওয়ার পর একই ইভেন্ট লুপে হালকা কোনো কাজ (যেমন ওয়েলকাম ইমেইল পাঠানো) ব্যাকগ্রাউন্ডে রান করা যায়। এর জন্য আলাদা কোনো সেটআপ লাগে না। অন্যদিকে Celery (Redis/RabbitMQ এর সাথে) হলো একটি শক্তিশালী ডিস্ট্রিবিউটেড টাস্ক কিউ। ভিডিও প্রসেসিংয়ের মতো ভারী বা দীর্ঘ মেয়াদী কাজের জন্য Celery বাধ্যতামূলক, তা না হলে FastAPI সার্ভার ব্লক হয়ে যাবে। এটি টাস্ক রিট্রাই (retry), শিডিউলিং এবং একাধিক সার্ভারে কাজ ভাগ করার সুবিধা দেয়।
বিস্তারিত উত্তর:
BackgroundTasks: এটিfastapiথেকে ইমপোর্ট করা একটি ক্লাস। রাউটে এটি ইনজেক্ট করে কোনো ফাংশন দিলে, HTTP রেসপন্স পাঠানোর ঠিক পরপরই FastAPI সেটি ব্যাকগ্রাউন্ডে রান করে। তবে টাস্ক চলাকালীন সার্ভার ক্র্যাশ করলে ওই টাস্কটি চিরতরে হারিয়ে যায়।- Celery: কোনো কাজে যদি ৫ মিনিট সময় লাগে, তবে
BackgroundTasksব্যবহার করা উচিত নয়, কারণ এটি আপনার সার্ভারের মেমরি ও থ্রেড আটকে রাখবে। Celery পুরো কাজটিকে FastAPI থেকে সরিয়ে একটি আলাদা ওয়ার্কার প্রসেসে নিয়ে যায়। সার্ভার ক্র্যাশ করলেও টাস্কগুলো কিউ-তে সেভ থাকে।
Python
# বাংলা কোড এক্সাম্পল: FastAPI BackgroundTasks
from fastapi import FastAPI, BackgroundTasks
import timeapp = FastAPI()# একটি সাধারণ টাস্ক ফাংশন
def send_email_notification(email: str):
time.sleep(3) # ৩ সেকেন্ডের বিলম্ব সিমুলেট করা হচ্ছে
print(f"{email} এ ইমেইল পাঠানো হয়েছে")@app.post("/register")
async def register_user(email: str, bg_tasks: BackgroundTasks):
# ১. ব্যাকগ্রাউন্ডে রান করার জন্য টাস্কটি যুক্ত করা
bg_tasks.add_task(send_email_notification, email)
# ২. ইমেইল যাওয়ার ৩ সেকেন্ডের জন্য অপেক্ষা না করে সাথে সাথেই রেসপন্স রিটার্ন করা
return {"message": "রেজিস্ট্রেশন সফল। কিছুক্ষণের মধ্যে ইমেইল পাঠানো হবে।"}
Question 15: How do you handle Custom Exception handling globally in FastAPI?
Short Answer:
Instead of scattering try/catch blocks across every route, FastAPI allows you to define custom exception classes and register Global Exception Handlers using the @app.exception_handler() decorator. When any route raises that specific custom exception, FastAPI intercepts it globally and formats a consistent, clean JSON response for the frontend, ensuring unified error formatting across the entire application.
Detailed Answer:
In a production app, consistent error structures (like always returning {"error_code": "X", "message": "Y"}) are vital for frontend developers.
- The Custom Exception: First, you inherit from Python’s standard
Exceptionclass to create something likeUserNotFoundError. - The Handler: You define an async function decorated with
@app.exception_handler(UserNotFoundError). Whenever youraise UserNotFoundError()anywhere in your code, FastAPI catches it, bypasses normal execution, and runs your handler to return a formattedJSONResponse.
Python
# English Code Example: Global Exception Handling
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponseapp = FastAPI()# 1. Define a Custom Exception Class
class ProductNotFoundError(Exception):
def __init__(self, product_id: int):
self.product_id = product_id# 2. Register the Global Exception Handler
@app.exception_handler(ProductNotFoundError)
async def product_not_found_handler(request: Request, exc: ProductNotFoundError):
# Consistently format the error response globally
return JSONResponse(
status_code=404,
content={"error": "Not Found", "message": f"Product with ID {exc.product_id} does not exist."},
)# 3. Raise the exception in any route
@app.get("/products/{product_id}")
async def get_product(product_id: int):
if product_id == 99:
raise ProductNotFoundError(product_id=product_id) # Handled automatically!
return {"id": product_id, "name": "Test Product"}
প্রশ্ন ১৫: FastAPI-তে আপনি গ্লোবালি কাস্টম এক্সেপশন (Custom Exception) কীভাবে হ্যান্ডল করবেন?
সংক্ষিপ্ত উত্তর:
প্রতিটি রাউটের ভেতর বারবার try/catch ব্লক লেখার বদলে, FastAPI আপনাকে কাস্টম এক্সেপশন ক্লাস তৈরি করার এবং @app.exception_handler() ডেকোরেটর ব্যবহার করে Global Exception Handlers রেজিস্টার করার সুবিধা দেয়। অ্যাপ্লিকেশনের যেকোনো জায়গা থেকে ওই নির্দিষ্ট এক্সেপশনটি রেইজ (raise) করলে, FastAPI গ্লোবালি তা ধরে ফেলে এবং ফ্রন্টএন্ডের জন্য একটি সুন্দর, গোছানো JSON রেসপন্স পাঠায়।
বিস্তারিত উত্তর:
প্রোডাকশন অ্যাপে সব এররের স্ট্রাকচার (যেমন {"error_code": "X", "message": "Y"}) একই রকম হওয়া ফ্রন্টএন্ড ডেভেলপারদের জন্য খুবই জরুরি।
- কাস্টম এক্সেপশন: প্রথমে Python-এর স্ট্যান্ডার্ড
Exceptionক্লাস থেকে ইনহেরিট করেUserNotFoundError-এর মতো একটি ক্লাস বানাতে হয়। - হ্যান্ডলার: এরপর
@app.exception_handler(UserNotFoundError)ডেকোরেটর দিয়ে একটি ফাংশন ডিফাইন করতে হয়। কোডের যেকোনো জায়গা থেকে আপনিraise UserNotFoundError()কল করলেই FastAPI তা ধরে আপনার হ্যান্ডলারের মাধ্যমে একটিJSONResponseরিটার্ন করবে।
Python
# বাংলা কোড এক্সাম্পল: গ্লোবাল এরর হ্যান্ডলিং
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponseapp = FastAPI()# ১. কাস্টম এক্সেপশন ক্লাস তৈরি
class ProductNotFoundError(Exception):
def __init__(self, product_id: int):
self.product_id = product_id# ২. গ্লোবাল এক্সেপশন হ্যান্ডলার রেজিস্টার করা
@app.exception_handler(ProductNotFoundError)
async def product_not_found_handler(request: Request, exc: ProductNotFoundError):
# গ্লোবালি এরর রেসপন্সকে একটি ফিক্সড ফরম্যাটে সাজানো
return JSONResponse(
status_code=404,
content={"error": "Not Found", "message": f"{exc.product_id} আইডির কোনো প্রোডাক্ট নেই।"},
)# ৩. যেকোনো রাউট থেকে এক্সেপশন রেইজ করা
@app.get("/products/{product_id}")
async def get_product(product_id: int):
if product_id == 99:
raise ProductNotFoundError(product_id=product_id) # স্বয়ংক্রিয়ভাবে হ্যান্ডল হয়ে যাবে!
return {"id": product_id, "name": "Test Product"}
Question 16: How do you integrate a relational database (like PostgreSQL/MySQL) with FastAPI using SQLAlchemy?
Short Answer:
FastAPI doesn’t have a built-in ORM, so we typically use SQLAlchemy. The integration involves three main parts:
- Setting up the SQLAlchemy
engineandsessionmakerto connect to the database. - Creating declarative mapping classes (Models) that represent the database tables.
- Using FastAPI’s
Depends()to inject an isolated database session (yield db) into each route handler, ensuring the connection is securely opened when the request starts and closed when it finishes.
Detailed Answer:
As your apps grow beyond simple scripts, managing database sessions correctly is vital to prevent connection leaks.
- The Session Dependency: We create a function
get_db()that uses Python'syieldgenerator. When a request comes in, it creates a new database session. Theyieldpasses the session to the route. Once the FastAPI route finishes returning the data to the user, the code afteryieldruns (db.close()), safely returning the connection to the pool.
Python
# English Code Example: SQLAlchemy Integration
from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, sessionmaker, Session# 1. SQLAlchemy Setup
DATABASE_URL = "sqlite:///./test.db" # Using SQLite for example
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()# 2. Database Model
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)Base.metadata.create_all(bind=engine) # Create tablesapp = FastAPI()# 3. Dependency to get DB Session safely
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close() # Ensures connection is closed after request# 4. Route using the injected DB session
@app.get("/users/")
def read_users(db: Session = Depends(get_db)):
users = db.query(User).all()
return users
প্রশ্ন ১৬: SQLAlchemy ব্যবহার করে আপনি কীভাবে একটি রিলেশনাল ডেটাবেস (PostgreSQL/MySQL) FastAPI এর সাথে ইন্টিগ্রেট করবেন?
সংক্ষিপ্ত উত্তর:
FastAPI এর নিজস্ব কোনো ORM নেই, তাই আমরা সাধারণত SQLAlchemy ব্যবহার করি। এর ইন্টিগ্রেশন মূলত ৩টি ধাপে হয়:
১. ডেটাবেসে কানেক্ট করার জন্য SQLAlchemy-এর engine এবং sessionmaker সেটআপ করা।
২. ডেটাবেস টেবিল রিপ্রেজেন্ট করার জন্য ডিক্ল্যারেটিভ ম্যাপিং ক্লাস (Models) তৈরি করা।
৩. FastAPI-এর Depends() ব্যবহার করে প্রতিটি রাউটে একটি আইসোলেটেড ডেটাবেস সেশন (yield db) ইনজেক্ট করা, যা নিশ্চিত করে রিকোয়েস্টের শুরুতে কানেকশন তৈরি হবে এবং শেষে তা নিরাপদে বন্ধ হয়ে যাবে।
বিস্তারিত উত্তর:
সাধারণ স্ক্রিপ্ট থেকে বেরিয়ে যখন আপনি বড় অ্যাপ বানাবেন, তখন কানেকশন লিক ঠেকাতে ডেটাবেস সেশন ঠিকমতো ম্যানেজ করা খুবই জরুরি।
- সেশন ডিপেন্ডেন্সি: আমরা
get_db()নামে একটি ফাংশন বানাই যা Python-এরyieldজেনারেটর ব্যবহার করে। রিকোয়েস্ট এলে এটি নতুন সেশন তৈরি করে রাউটে পাঠায়। FastAPI ইউজারকে ডেটা রিটার্ন করার পর,yieldএর পরের কোডটি (db.close()) রান হয় এবং কানেকশনটি নিরাপদে পুলে ফেরত যায়।
Python
# বাংলা কোড এক্সাম্পল: SQLAlchemy ইন্টিগ্রেশন
from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, sessionmaker, Session# ১. SQLAlchemy সেটআপ
DATABASE_URL = "sqlite:///./test.db" # উদাহরণের জন্য SQLite ব্যবহার করা হলো
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()# ২. ডেটাবেস মডেল
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)Base.metadata.create_all(bind=engine) # টেবিল তৈরি করাapp = FastAPI()# ৩. নিরাপদে DB Session পাওয়ার ডিপেন্ডেন্সি
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close() # রিকোয়েস্ট শেষে কানেকশন ক্লোজ নিশ্চিত করা# ৪. ইনজেক্ট করা DB সেশন ব্যবহার করে রাউট তৈরি
@app.get("/users/")
def read_users(db: Session = Depends(get_db)):
users = db.query(User).all()
return users
Question 17: What are Pydantic Field validations and Custom Validators (@field_validator)?
Short Answer:
While standard Pydantic models validate basic data types (e.g., ensuring an int is a number), Field Validations and Custom Validators enforce stricter business rules. Field() allows you to set limits like minimum length or constraints on values directly. The @field_validator decorator allows you to write custom Python logic (e.g., checking if a password contains a special character) to validate a specific field before the data is accepted.
Detailed Answer:
Validating data types isn’t enough; you must validate business logic.
Field(): It is great for basic constraints:age: int = Field(gt=18)ensures the age is greater than 18.username: str = Field(min_length=3)enforces string length.@field_validator: Sometimes constraints are complex. If a user submits a username, you might want to ensure it doesn't contain spaces. You use this decorator over a class method inside the Pydantic model. If your custom logic fails, you raise aValueError, which FastAPI instantly translates into a clean422 Unprocessable EntityAPI response.
Python
# English Code Example: Pydantic Validators
from fastapi import FastAPI
from pydantic import BaseModel, Field, field_validatorapp = FastAPI()class UserRegistration(BaseModel):
# 1. Basic Field constraints
username: str = Field(..., min_length=3, max_length=15)
age: int = Field(..., gt=18, description="Must be over 18")
# 2. Custom logical validator
@field_validator('username')
@classmethod
def username_must_not_contain_spaces(cls, v: str) -> str:
if " " in v:
raise ValueError('Username must not contain spaces')
return v.title() # You can also modify/clean data here!@app.post("/register")
async def register(user: UserRegistration):
return user
প্রশ্ন ১৭: Pydantic Field validations এবং Custom Validators (@field_validator) কী?
সংক্ষিপ্ত উত্তর:
সাধারণ Pydantic মডেল শুধু বেসিক ডেটা টাইপ চেক করে (যেমন- ভ্যালুটি নাম্বার কিনা)। কিন্তু Field Validations এবং Custom Validators আরও কড়া বিজনেস রুলস প্রয়োগ করতে ব্যবহৃত হয়। Field() ব্যবহার করে আপনি সরাসরি মিনিমাম লেংথ বা ভ্যালুর লিমিট সেট করতে পারেন। আর @field_validator ডেকোরেটর ব্যবহার করে আপনি কাস্টম Python লজিক লিখতে পারেন (যেমন- পাসওয়ার্ডে কোনো স্পেশাল ক্যারেক্টার আছে কিনা), যা ডেটা রিসিভ করার আগেই ভ্যালিডেট করে নেয়।
বিস্তারিত উত্তর:
শুধু ডেটা টাইপ ভ্যালিডেট করাই যথেষ্ট নয়, বিজনেস লজিকও চেক করতে হয়।
Field(): এটি সাধারণ রুলসের জন্য দারুণ।age: int = Field(gt=18)নিশ্চিত করে বয়স ১৮ এর বেশি।username: str = Field(min_length=3)স্ট্রিংয়ের লেংথ ঠিক রাখে।@field_validator: মাঝেমধ্যে কন্ডিশন জটিল হয়। ইউজারনেমে কোনো স্পেস আছে কিনা তা চেক করতে চাইলে, Pydantic মডেলের ভেতর একটি মেথডের ওপর এই ডেকোরেটর ব্যবহার করতে হয়। কাস্টম লজিক ফেইল করলে আপনিValueErrorরেইজ করবেন, আর FastAPI সেটিকে সুন্দর করে422 Unprocessable Entityরেসপন্সে বদলে দেবে।
Python
# বাংলা কোড এক্সাম্পল: Pydantic ভ্যালিডেটরস
from fastapi import FastAPI
from pydantic import BaseModel, Field, field_validatorapp = FastAPI()class UserRegistration(BaseModel):
# ১. সাধারণ Field কনস্ট্রেইন্ট
username: str = Field(..., min_length=3, max_length=15)
age: int = Field(..., gt=18, description="বয়স অবশ্যই ১৮ এর বেশি হতে হবে")
# ২. কাস্টম লজিক্যাল ভ্যালিডেটর
@field_validator('username')
@classmethod
def username_must_not_contain_spaces(cls, v: str) -> str:
if " " in v:
raise ValueError('ইউজারনেমে কোনো স্পেস থাকা যাবে না')
return v.title() # আপনি চাইলে ডেটা মডিফাই বা ক্লিনও করতে পারেন!@app.post("/register")
async def register(user: UserRegistration):
return user
Question 18: What is the architectural difference between Middleware and Dependency Injection in FastAPI?
Short Answer:
While both intercept incoming requests, they operate at completely different layers. Middleware runs globally at the application network layer; it catches every single HTTP request before routing occurs, making it ideal for cross-cutting concerns like CORS, logging, or timing requests. Dependency Injection (Depends) operates at the specific route layer; it only runs for routes that explicitly request it, making it ideal for fine-grained business logic like database sessions, extracting query parameters, or verifying specific user roles.
Detailed Answer:
Understanding when to use which separates beginners from seniors.
- Middleware: Think of it as a gateway. A custom middleware function receives the raw HTTP
request, passes it forward usingawait call_next(request), gets theresponseback, and can modify headers before sending it out. If an error occurs in Middleware, the request never even reaches the FastAPI router. - Dependency Injection: Think of it as a helper for specific endpoints. If only 3 routes out of 50 require checking an API Key, you use a Dependency on those 3 routes. You wouldn’t put that check in Middleware because the Middleware would block the other 47 public routes unnecessarily.
প্রশ্ন ১৮: FastAPI-তে Middleware এবং Dependency Injection এর মধ্যে আর্কিটেকচারাল পার্থক্য কী?
সংক্ষিপ্ত উত্তর:
দুটি পদ্ধতিই রিকোয়েস্ট রিসিভ করার পর কাজ করে, তবে এরা সম্পূর্ণ ভিন্ন লেয়ারে কাজ করে। Middleware অ্যাপ্লিকেশনের নেটওয়ার্ক লেয়ারে গ্লোবালি কাজ করে; রাউটিং শুরু হওয়ার আগেই এটি প্রতিটি HTTP রিকোয়েস্ট ধরে ফেলে। তাই CORS, লগিং বা রিকোয়েস্টের টাইম মাপার জন্য এটি সেরা। অন্যদিকে Dependency Injection (Depends) নির্দিষ্ট রাউট লেয়ারে কাজ করে; এটি শুধু সেই রাউটের জন্যই রান হয় যেটিতে اسے কল করা হয়েছে। ডেটাবেস সেশন, কুয়েরি প্যারামিটার বা নির্দিষ্ট ইউজারের রোল চেক করার মতো নিখুঁত বিজনেস লজিকের জন্য এটি ব্যবহৃত হয়।
বিস্তারিত উত্তর:
কখন কোনটি ব্যবহার করতে হবে তা জানাই একজন সিনিয়র ডেভেলপারের কাজ।
- Middleware: একে একটি মূল গেটওয়ে বা প্রবেশদ্বার হিসেবে চিন্তা করুন। এটি র (raw) HTTP
requestরিসিভ করে,await call_next(request)দিয়ে সামনের দিকে পাঠায়, আবারresponseফেরত পেয়ে হেডারে পরিবর্তন এনে ইউজারের কাছে পাঠাতে পারে। মিডলওয়্যারে কোনো এরর হলে রিকোয়েস্টটি রাউটার পর্যন্ত পৌঁছাতেই পারে না। - Dependency Injection: একে নির্দিষ্ট এন্ডপয়েন্টের সাহায্যকারী হিসেবে ভাবুন। ৫০টি রাউটের মধ্যে যদি মাত্র ৩টি রাউটে API Key চেক করতে হয়, তবে আপনি ওই ৩টিতে ডিপেন্ডেন্সি ব্যবহার করবেন। আপনি মিডলওয়্যারে এই চেকিং বসাবেন না, কারণ তাহলে বাকি ৪৭টি পাবলিক রাউটও ব্লক হয়ে যাবে।
Question 19: How do you manage Environment Variables and Secrets securely in FastAPI? Explain BaseSettings.
Short Answer:
To securely manage configurations without hardcoding secrets, we use environment variables. In FastAPI, the most robust architectural pattern is using Pydantic’s BaseSettings (from pydantic-settings). It automatically reads variables from .env files or system environments, strictly validates their data types, provides default values, and allows you to inject configurations safely throughout your application as a typed object.
Detailed Answer:
Relying purely on os.getenv("DB_URL") is risky because if the variable is missing or is an empty string, your app will crash deep inside the code at runtime.
- The Pydantic Approach: You create a
Settingsclass inheriting fromBaseSettings. IfDATABASE_URLis missing from the.envfile, FastAPI will refuse to start the server, failing fast and giving you a clear validation error immediately. - Type Coercion: If you have
MAX_USERS=100in your.envfile,os.getenvreads it as a string"100".BaseSettingswill automatically convert it to a Pythonintbased on your type hints.
Python
# English Code Example: Pydantic BaseSettings
# Note: Requires: pip install pydantic-settings
from pydantic_settings import BaseSettings
from functools import lru_cache# 1. Define the strictly typed Settings class
class Settings(BaseSettings):
app_name: str = "My FastAPI Project" # Default value
database_url: str # Required from .env
debug_mode: bool = False # Coerced to boolean from "True"/"False" string class Config:
env_file = ".env"# 2. Cache it so it only reads the file once
@lru_cache
def get_settings():
return Settings()# Usage
settings = get_settings()
print(f"Connecting to: {settings.database_url}")
প্রশ্ন ১৯: FastAPI-তে আপনি Environment Variables এবং Secrets কীভাবে নিরাপদে ম্যানেজ করবেন? BaseSettings এর কাজ কী?
সংক্ষিপ্ত উত্তর:
সিক্রেট কি (keys) কোডে হার্ডকোড না করে নিরাপদে ম্যানেজ করার জন্য আমরা এনভায়রনমেন্ট ভেরিয়েবল ব্যবহার করি। FastAPI-তে এর সবচেয়ে শক্তিশালী আর্কিটেকচারাল প্যাটার্ন হলো Pydantic-এর BaseSettings (pydantic-settings থেকে) ব্যবহার করা। এটি স্বয়ংক্রিয়ভাবে .env ফাইল বা সিস্টেম থেকে ভেরিয়েবল পড়ে, তাদের ডেটা টাইপ কড়াকড়িভাবে ভ্যালিডেট করে, ডিফল্ট ভ্যালু দেয় এবং পুরো অ্যাপ্লিকেশনে একটি টাইপ-সেফ অবজেক্ট হিসেবে কনফিগারেশনগুলো ব্যবহার করার সুযোগ দেয়।
বিস্তারিত উত্তর:
শুধু os.getenv("DB_URL") এর ওপর নির্ভর করা ঝুঁকিপূর্ণ, কারণ ভেরিয়েবলটি মিসিং থাকলে বা খালি স্ট্রিং হলে আপনার অ্যাপ রানটাইমে গিয়ে হঠাৎ ক্র্যাশ করবে।
- Pydantic পদ্ধতি: আপনি
BaseSettingsথেকে ইনহেরিট করে একটিSettingsক্লাস বানান। যদি.envফাইলেDATABASE_URLনা থাকে, তবে FastAPI সার্ভার চালু হতেই দেবে না এবং শুরুতেই আপনাকে একটি পরিষ্কার ভ্যালিডেশন এরর দেখাবে। - টাইপ কনভার্শন: আপনার
.envফাইলে যদিMAX_USERS=100থাকে,os.getenvসেটিকে স্ট্রিং"100"হিসেবে পড়ে। কিন্তুBaseSettingsটাইপ হিন্টস দেখে নিজে থেকেই এটিকে Pythonintএ কনভার্ট করে নেবে।
Python
# বাংলা কোড এক্সাম্পল: Pydantic BaseSettings
# নোট: এর জন্য pip install pydantic-settings ইনস্টল করতে হবে
from pydantic_settings import BaseSettings
from functools import lru_cache# ১. টাইপ-সেফ Settings ক্লাস তৈরি করা
class Settings(BaseSettings):
app_name: str = "My FastAPI Project" # ডিফল্ট ভ্যালু
database_url: str # .env থেকে পাওয়া বাধ্যতামূলক
debug_mode: bool = False # "True" বা "False" স্ট্রিং থেকে বুলিয়ানে কনভার্ট হবে class Config:
env_file = ".env"# ২. ফাইলটি যেন বারবার রিড না হয়, তাই ক্যাশ করে রাখা
@lru_cache
def get_settings():
return Settings()# ব্যবহার
settings = get_settings()
print(f"Connecting to: {settings.database_url}")
Question 20: What are Python Magic Methods (Dunder methods) like __init__ and __str__, and why are they important in Object-Oriented Python?
Short Answer:
Magic methods, also called “Dunder” (Double Underscore) methods, are special built-in methods in Python that start and end with double underscores (e.g., __init__, __str__, __len__). They allow developers to define how instances of custom classes interact with core Python operators and built-in functions. They are the backbone of Python's data model and Object-Oriented Programming (OOP).
Detailed Answer:
Understanding dunder methods elevates your Python skills beyond basic scripting.
__init__(Constructor): This is the most common dunder method. It is automatically called when a new object of a class is instantiated. It is used to initialize the object's attributes.__str__(String Representation): If youprint(my_object), Python usually outputs a useless memory address like<__main__.User object at 0x...>. By definingdef __str__(self):, you override this behavior to return a readable, user-friendly string (e.g.,"User: John Doe").- Why it matters: Frameworks heavily rely on these. Pydantic models and SQLAlchemy models use complex dunder methods under the hood to enable magic behaviors like attribute validation and database mapping.
Python
# English Code Example: Dunder Methods
class Employee:
# 1. The constructor dunder method
def __init__(self, name: str, salary: int):
self.name = name
self.salary = salary# 2. The string representation dunder method
def __str__(self):
return f"Employee(Name: {self.name}, Salary: ${self.salary})"
# 3. Adding custom behavior to the '+' operator
def __add__(self, other_employee):
return self.salary + other_employee.salary# Usage
emp1 = Employee("Alice", 5000)
emp2 = Employee("Bob", 6000)print(emp1) # Automatically calls __str__, outputs: Employee(Name: Alice, Salary: $5000)
print(emp1 + emp2) # Automatically calls __add__, outputs: 11000
প্রশ্ন ২০: Python-এ Magic Methods (Dunder methods) যেমন __init__ এবং __str__ কী, এবং অবজেক্ট-ওরিয়েন্টেড Python-এ এগুলো কেন গুরুত্বপূর্ণ?
সংক্ষিপ্ত উত্তর:
ম্যাজিক মেথড, যাকে “Dunder” (Double Underscore) মেথডও বলা হয়, হলো Python-এর কিছু বিশেষ বিল্ট-ইন মেথড যার শুরু এবং শেষে ডাবল আন্ডারস্কোর থাকে (যেমন __init__, __str__, __len__)। কাস্টম ক্লাসের অবজেক্টগুলো Python-এর কোর অপারেটর বা বিল্ট-ইন ফাংশনের সাথে কীভাবে কাজ করবে, তা নির্ধারণ করতে ডেভেলপাররা এগুলো ব্যবহার করেন। এগুলো মূলত Python-এর অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিংয়ের (OOP) মূল ভিত্তি।
বিস্তারিত উত্তর:
Get Yeasaleh’s stories in your inbox
Join Medium for free to get updates from this writer.
ডান্ডার মেথডগুলো বুঝতে পারলে আপনার Python স্কিল অনেক অ্যাডভান্সড লেভেলে চলে যাবে।
__init__(কনস্ট্রাক্টর): এটি সবচেয়ে বেশি ব্যবহৃত ডান্ডার মেথড। কোনো ক্লাসের নতুন অবজেক্ট তৈরি হওয়ার সাথে সাথেই এটি নিজে থেকে কল হয়। অবজেক্টের ইনিশিয়াল অ্যাট্রিবিউট বা ডেটা সেট করার জন্য এটি ব্যবহৃত হয়।__str__(স্ট্রিং রিপ্রেজেন্টেশন): আপনি যদিprint(my_object)করেন, সাধারণ Python<__main__.User object at 0x...>এর মতো অর্থহীন মেমরি অ্যাড্রেস দেখাবে। কিন্তুdef __str__(self):লিখে আপনি এর আচরণ পাল্টে দিতে পারেন, যাতে এটি মানুষের পড়ার উপযোগী সুন্দর একটি স্ট্রিং (যেমন"User: John Doe") রিটার্ন করে।- গুরুত্ব: বড় ফ্রেমওয়ার্কগুলো এর ওপর প্রচুর নির্ভরশীল। Pydantic বা SQLAlchemy-এর মতো লাইব্রেরিগুলো ডেটা ভ্যালিডেশন বা ডেটাবেস ম্যাপিংয়ের ম্যাজিক দেখানোর জন্য ভেতরে ভেতরে এই ডান্ডার মেথডগুলোই ব্যবহার করে।
Python
# বাংলা কোড এক্সাম্পল: Dunder Methods এর ব্যবহার
class Employee:
# ১. কনস্ট্রাক্টর ডান্ডার মেথড
def __init__(self, name: str, salary: int):
self.name = name
self.salary = salary# ২. স্ট্রিং রিপ্রেজেন্টেশন ডান্ডার মেথড
def __str__(self):
return f"Employee(Name: {self.name}, Salary: ${self.salary})"
# ৩. '+' অপারেটরের আচরণ পরিবর্তন করার ডান্ডার মেথড
def __add__(self, other_employee):
return self.salary + other_employee.salary# ব্যবহার
emp1 = Employee("Alice", 5000)
emp2 = Employee("Bob", 6000)print(emp1) # নিজে থেকেই __str__ কল করবে, আউটপুট: Employee(Name: Alice, Salary: $5000)
print(emp1 + emp2) # নিজে থেকেই __add__ কল করবে, আউটপুট: 11000
(এই হলো Python এবং FastAPI-এর Phase 2! আপনি প্রস্তুত হলে জানাবেন, আমি Phase 3 জেনারেট করব, যেখানে Authentication (JWT), WebSockets, Database Migrations (Alembic) এবং আরও অ্যাডভান্সড টপিক থাকবে!)
এখানে আপনার Python এবং FastAPI সিরিজের Phase 3 (Q21 থেকে Q30) দেওয়া হলো।
যেহেতু আপনি ৬ মাস ধরে Python নিয়ে কাজ করছেন, তাই এই পর্বে আমরা প্রফেশনাল প্রোজেক্টের কিছু অপরিহার্য বিষয় নিয়ে আলোচনা করব — যেমন Database Migrations (Alembic), JWT Authentication, WebSockets, Testing এবং Python-এর কিছু অ্যাডভান্সড কনসেপ্ট।
Phase 3: Authentication, WebSockets, Migrations & Testing (6-Month Experience Level)
Question 21: How do you handle Database Migrations in a FastAPI + SQLAlchemy project? Explain Alembic.
Short Answer:
Since SQLAlchemy does not automatically update your database tables when you change your Python models (e.g., adding a new column), we use a migration tool called Alembic. It tracks changes made to your SQLAlchemy models, generates sequential Python scripts (migration files) describing those changes, and safely applies them to your database to keep the schema in sync with your code.
Detailed Answer:
Relying on Base.metadata.create_all(engine) is fine for beginners, but in production, it will not alter existing tables.
- Setup: You initialize Alembic in your project (
alembic init alembic). - Tracking Changes: When you add a new
agecolumn to yourUsermodel, you runalembic revision --autogenerate -m "added age". Alembic compares your Python code to the actual database schema and creates a script containing the exact SQL commands needed (likeALTER TABLE). - Executing: You run
alembic upgrade headto safely apply the changes to the database. If something breaks, you can easily rollback usingalembic downgrade.
Bash
# English Code Example: Alembic Workflow
# 1. Initialize Alembic
alembic init alembic# 2. Generate a migration script after changing models
alembic revision --autogenerate -m "added user age column"# 3. Apply the migration to the database
alembic upgrade head
প্রশ্ন ২১: FastAPI এবং SQLAlchemy প্রোজেক্টে আপনি Database Migrations কীভাবে হ্যান্ডল করেন? Alembic বুঝিয়ে বলুন।
সংক্ষিপ্ত উত্তর:
আপনি যখন Python মডেলে কোনো পরিবর্তন করেন (যেমন নতুন কলাম যোগ করা), SQLAlchemy নিজে থেকে ডেটাবেস টেবিল আপডেট করতে পারে না। এর জন্য আমরা Alembic নামের একটি মাইগ্রেশন টুল ব্যবহার করি। এটি SQLAlchemy মডেলের পরিবর্তনগুলো ট্র্যাক করে, সেই অনুযায়ী কিছু সিরিয়াল Python স্ক্রিপ্ট (মাইগ্রেশন ফাইল) তৈরি করে এবং নিরাপদে সেগুলো ডেটাবেসে রান করে ডেটাবেস স্কিমাকে কোডের সাথে সিঙ্ক রাখে।
বিস্তারিত উত্তর:
Base.metadata.create_all(engine) শুধু নতুন প্রোজেক্টের জন্য ঠিক আছে, কিন্তু প্রোডাকশনে এটি তৈরি করা টেবিল মডিফাই করতে পারে না।
১. সেটআপ: প্রোজেক্টে প্রথমে Alembic ইনিশিয়ালাইজ করতে হয় (alembic init alembic)।
২. পরিবর্তন ট্র্যাক করা: User মডেলে নতুন age কলাম যোগ করার পর আপনি alembic revision --autogenerate -m "added age" কমান্ডটি রান করবেন। Alembic আপনার কোডের সাথে ডেটাবেস স্কিমা মিলিয়ে দেখবে এবং ঠিক কী পরিবর্তন (যেমন ALTER TABLE) করতে হবে তার একটি স্ক্রিপ্ট তৈরি করবে।
৩. এক্সিকিউট করা: এরপর alembic upgrade head রান করলে এটি নিরাপদে ডেটাবেস আপডেট করে দেবে। কোনো সমস্যা হলে alembic downgrade করে আগের অবস্থায় ফিরে যাওয়া যায়।
Bash
# বাংলা কোড এক্সাম্পল: Alembic ওয়ার্কফ্লো
# ১. Alembic ইনিশিয়ালাইজ করা
alembic init alembic# ২. মডেল পরিবর্তন করার পর মাইগ্রেশন স্ক্রিপ্ট তৈরি করা
alembic revision --autogenerate -m "ইউজার মডেলে বয়স যোগ করা হলো"# ৩. ডেটাবেসে মাইগ্রেশন রান বা অ্যাপ্লাই করা
alembic upgrade head
Question 22: How do you implement JWT Authentication in FastAPI using OAuth2?
Short Answer:
FastAPI provides built-in tools for OAuth2 integration. Architecturally, we use the OAuth2PasswordBearer class as a Dependency. When applied to a route, FastAPI automatically looks for an Authorization: Bearer <token> header in the incoming request. If found, it extracts the token, which you can then cryptographically verify (using the jose or PyJWT library) to authenticate the user.
Detailed Answer:
FastAPI makes building secure APIs incredibly straightforward.
- The Token URL: You configure
OAuth2PasswordBearer(tokenUrl="login"). This tells FastAPI's auto-generated Swagger UI exactly which endpoint to hit to get the token. - The Route Dependency: You inject this into your route:
token: str = Depends(oauth2_scheme). If the user does not provide a token, FastAPI immediately throws a401 Unauthorizederror before your code even runs. - Verification: Once you receive the raw token string from the dependency, you decode it. If valid, you query the database to find the user and return the user object to the route.
প্রশ্ন ২২: OAuth2 ব্যবহার করে FastAPI-তে আপনি JWT Authentication কীভাবে ইমপ্লিমেন্ট করবেন?
সংক্ষিপ্ত উত্তর:
FastAPI-তে OAuth2 ইন্টিগ্রেট করার জন্য নিজস্ব টুল রয়েছে। আর্কিটেকচারালি, আমরা OAuth2PasswordBearer ক্লাসটিকে একটি ডিপেন্ডেন্সি (Dependency) হিসেবে ব্যবহার করি। কোনো রাউটে এটি যুক্ত করলে, FastAPI রিকোয়েস্টের হেডারে নিজে থেকেই Authorization: Bearer <token> খুঁজবে। টোকেন পেলে সে তা বের করে আনবে, যা পরে আপনি jose বা PyJWT লাইব্রেরি দিয়ে ভেরিফাই করে ইউজারকে লগইন করাতে পারবেন।
বিস্তারিত উত্তর:
FastAPI দিয়ে সিকিউর API বানানো খুবই সহজ।
১. টোকেন URL: আপনি OAuth2PasswordBearer(tokenUrl="login") কনফিগার করেন। এটি Swagger UI-কে জানিয়ে দেয় যে লগইন করার জন্য ঠিক কোন এন্ডপয়েন্টে হিট করতে হবে।
২. রাউট ডিপেন্ডেন্সি: রাউটে আপনি token: str = Depends(oauth2_scheme) ইনজেক্ট করেন। ইউজার টোকেন না দিলে আপনার কোড রান করার আগেই FastAPI তাকে 401 Unauthorized এরর দিয়ে দেবে।
৩. ভেরিফিকেশন: ডিপেন্ডেন্সি থেকে টোকেনটি পাওয়ার পর আপনি তা ডিকোড করবেন। টোকেন ঠিক থাকলে ডেটাবেস থেকে ইউজারের ডেটা এনে রাউটে পাস করে দেবেন।
Question 23: How and why do we hash passwords in Python? Explain Passlib and Bcrypt.
Short Answer:
We never store plain-text passwords in a database because if the database gets compromised, hackers instantly have access to all user accounts. Instead, we hash them. Hashing is a one-way mathematical function (unlike encryption, which is two-way). In Python, we use the passlib library configured with the bcrypt algorithm. Bcrypt is specifically designed to be computationally slow, making brute-force or dictionary attacks practically impossible.
Detailed Answer:
Security relies on slowing attackers down.
- Hashing vs. Encryption: Encryption allows data to be decrypted back to its original form using a secret key. Hashing destroys the original data. You cannot reverse a hash.
- Using Passlib: When a user registers, we hash their password
hash = pwd_context.hash(password). We save the hash to the DB. - Verifying: When the user logs in, we don’t decrypt the DB hash. Instead, we hash the new password they just typed and compare it to the stored hash using
pwd_context.verify(plain_password, hashed_password).
Python
# English Code Example: Hashing with Passlib
# pip install passlib[bcrypt]
from passlib.context import CryptContext# Configure passlib to use the bcrypt algorithm
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")# 1. Hashing a password for registration
plain_password = "supersecretpassword123"
hashed_password = pwd_context.hash(plain_password)
print(f"Stored in DB: {hashed_password}")# 2. Verifying a password during login
is_valid = pwd_context.verify("supersecretpassword123", hashed_password)
print(f"Password correct: {is_valid}") # True
প্রশ্ন ২৩: Python-এ আমরা কীভাবে এবং কেন পাসওয়ার্ড হ্যাশ (Hash) করি? Passlib এবং Bcrypt বুঝিয়ে বলুন।
সংক্ষিপ্ত উত্তর:
আমরা কখনোই ডেটাবেসে প্লেইন-টেক্সটে (সরাসরি) পাসওয়ার্ড সেভ করি না, কারণ ডেটাবেস হ্যাক হলে হ্যাকাররা সব ইউজারের অ্যাকাউন্ট পেয়ে যাবে। এর বদলে আমরা পাসওয়ার্ড হ্যাশ করি। হ্যাশিং হলো একটি একমুখী গাণিতিক পদ্ধতি (এনক্রিপশনের মতো দ্বিমুখী নয়)। Python-এ আমরা passlib লাইব্রেরি এবং bcrypt অ্যালগরিদম ব্যবহার করি। Bcrypt ইচ্ছাকৃতভাবেই একটু ধীরগতির করে বানানো হয়েছে, যাতে হ্যাকাররা দ্রুত ব্রুট-ফোর্স (brute-force) বা ডিকশনারি অ্যাটাক করতে না পারে।
বিস্তারিত উত্তর:
সিকিউরিটির মূল লক্ষ্যই হলো হ্যাকারদের কাজ কঠিন করে দেওয়া।
১. হ্যাশিং বনাম এনক্রিপশন: এনক্রিপশনে সিক্রেট কি (key) দিয়ে ডেটাকে আগের অবস্থায় ফিরিয়ে আনা যায়। কিন্তু হ্যাশিং অরিজিনাল ডেটা পুরোপুরি নষ্ট করে দেয়। হ্যাশকে কখনোই রিভার্স করা যায় না।
২. Passlib এর ব্যবহার: ইউজার সাইন-আপ করার সময় আমরা পাসওয়ার্ড হ্যাশ করি: hash = pwd_context.hash(password)। এরপর এই হ্যাশটি ডেটাবেসে সেভ করি।
৩. ভেরিফিকেশন: ইউজার লগইন করার সময় আমরা ডেটাবেসের হ্যাশকে ডিক্রিপ্ট করি না। বরং ইউজার যে নতুন পাসওয়ার্ডটি দিল, সেটিকে আবার হ্যাশ করে ডেটাবেসের হ্যাশের সাথে মেলাই: pwd_context.verify(plain, hashed)।
Python
# বাংলা কোড এক্সাম্পল: Passlib দিয়ে হ্যাশিং
# pip install passlib[bcrypt]
from passlib.context import CryptContext# bcrypt অ্যালগরিদম ব্যবহারের জন্য passlib কনফিগার করা
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")# ১. রেজিস্ট্রেশনের সময় পাসওয়ার্ড হ্যাশ করা
plain_password = "supersecretpassword123"
hashed_password = pwd_context.hash(plain_password)
print(f"ডেটাবেসে সেভ হবে: {hashed_password}")# ২. লগইন করার সময় পাসওয়ার্ড ভেরিফাই করা
is_valid = pwd_context.verify("supersecretpassword123", hashed_password)
print(f"পাসওয়ার্ড সঠিক: {is_valid}") # True
Question 24: What are WebSockets, and how do you implement them in FastAPI?
Short Answer:
Unlike HTTP, which opens a connection, sends data, and immediately closes it, WebSockets establish a persistent, bidirectional (full-duplex) communication channel between the client and server. Both sides can push data instantly without polling. FastAPI fully supports this natively using the @app.websocket() decorator and the WebSocket class, allowing you to accept connections, receive messages, and broadcast data effortlessly.
Detailed Answer:
WebSockets are mandatory for chat apps or live dashboards.
- The Handshake: The client sends an HTTP request asking to “upgrade” to a WebSocket connection. In FastAPI, you call
await websocket.accept()to approve this upgrade. - The Loop: Because the connection stays open, you use an infinite
while True:loop in your Python code to continuously wait for and process incoming messages (await websocket.receive_text()). - Disconnection: If the client refreshes the page, the connection drops, raising a
WebSocketDisconnectexception which you must catch to remove the user from your active connections list.
Python
# English Code Example: Simple FastAPI WebSocket
from fastapi import FastAPI, WebSocket, WebSocketDisconnectapp = FastAPI()@app.websocket("/ws/chat")
async def websocket_endpoint(websocket: WebSocket):
# 1. Accept the incoming connection request
await websocket.accept()
try:
# 2. Infinite loop to keep the connection alive
while True:
# Wait for the client to send a message
data = await websocket.receive_text()
# Echo the message back to the client
await websocket.send_text(f"Message text was: {data}")
except WebSocketDisconnect:
# 3. Handle user disconnection gracefully
print("Client disconnected")
প্রশ্ন ২৪: WebSockets কী, এবং FastAPI-তে আপনি কীভাবে এটি ইমপ্লিমেন্ট করবেন?
সংক্ষিপ্ত উত্তর:
HTTP কানেকশন তৈরি করে ডেটা পাঠিয়ে সাথে সাথেই তা ক্লোজ করে দেয়। কিন্তু WebSockets ক্লায়েন্ট এবং সার্ভারের মধ্যে একটি স্থায়ী ও দ্বিমুখী (bidirectional) কানেকশন তৈরি করে। এতে বারবার রিকোয়েস্ট না পাঠিয়েও উভয় দিক থেকে তাৎক্ষণিকভাবে ডেটা পুশ করা যায়। FastAPI-তে @app.websocket() ডেকোরেটর এবং WebSocket ক্লাস ব্যবহার করে খুব সহজেই কানেকশন রিসিভ এবং ডেটা ব্রডকাস্ট করা যায়।
বিস্তারিত উত্তর:
চ্যাট অ্যাপ বা লাইভ ড্যাশবোর্ডের জন্য WebSockets বাধ্যতামূলক।
১. হ্যান্ডশেক (Handshake): ক্লায়েন্ট প্রথমে HTTP রিকোয়েস্ট দিয়ে কানেকশনটিকে “আপগ্রেড” করে ওয়েবসকেট করার অনুরোধ জানায়। FastAPI-তে await websocket.accept() দিয়ে এটি অ্যাপ্রুভ করতে হয়।
২. লুপ (Loop): যেহেতু কানেকশনটি সবসময় খোলা থাকে, তাই Python কোডে একটি ইনফিনিট (অসীম) while True: লুপ চালাতে হয়। এটি ক্লায়েন্ট থেকে আসা মেসেজের জন্য অপেক্ষা করে (await websocket.receive_text())।
৩. ডিসকানেকশন: ইউজার পেজ রিফ্রেশ দিলে কানেকশন কেটে যায় এবং একটি WebSocketDisconnect এক্সেপশন থ্রো হয়। এটি try/catch দিয়ে ধরে অ্যাকটিভ ইউজারের লিস্ট থেকে তাকে রিমুভ করতে হয়।
Python
# বাংলা কোড এক্সাম্পল: সাধারণ FastAPI WebSocket
from fastapi import FastAPI, WebSocket, WebSocketDisconnectapp = FastAPI()@app.websocket("/ws/chat")
async def websocket_endpoint(websocket: WebSocket):
# ১. কানেকশন রিকোয়েস্ট অ্যাক্সেপ্ট করা
await websocket.accept()
try:
# ২. কানেকশন সচল রাখার জন্য ইনফিনিট লুপ
while True:
# ক্লায়েন্ট মেসেজ পাঠানো পর্যন্ত অপেক্ষা করা
data = await websocket.receive_text()
# ক্লায়েন্টকে মেসেজটি রিপ্লাই দেওয়া
await websocket.send_text(f"আপনার মেসেজটি ছিল: {data}")
except WebSocketDisconnect:
# ৩. ক্লায়েন্ট ডিসকানেক্ট হলে সুন্দরভাবে হ্যান্ডল করা
print("ক্লায়েন্ট ডিসকানেক্ট হয়ে গেছে")
Question 25: What is a Generator in Python (yield), and how is it different from a normal function (return)?
Short Answer:
A normal function uses return to send a single value back to the caller, instantly terminating the function and destroying its local variables. A Generator function uses the yield keyword. It returns a value, but instead of terminating, it pauses its execution and remembers its state. The next time it is called, it resumes exactly where it left off. Generators are massively memory-efficient for iterating over large datasets.
Detailed Answer:
In FastAPI, yield is fundamentally used for handling Database sessions in dependencies.
- The Memory Difference: If you read a 10GB log file into a standard list and
returnit, Python loads 10GB into RAM and crashes. If youyieldone line at a time, Python only ever uses a few megabytes of RAM. - FastAPI Dependencies: When we write
yield dbin a FastAPI database dependency, FastAPI pauses the dependency, sends thedbsession to the route, lets the route finish, and then resumes the dependency to rundb.close()after the response has been sent to the user.
Python
# English Code Example: Generator vs Normal Function
def countdown_generator(n):
while n > 0:
yield n # Pauses here, returns the number, keeps state
n -= 1# Using the generator
gen = countdown_generator(3)
print(next(gen)) # Output: 3
print(next(gen)) # Output: 2
# State is preserved between calls without storing all numbers in a list!প্রশ্ন ২৫: Python-এ জেনারেটর (Generator — yield) কী, এবং সাধারণ ফাংশনের (return) চেয়ে এটি কীভাবে আলাদা?
সংক্ষিপ্ত উত্তর:
সাধারণ ফাংশনে return ব্যবহার করলে সেটি একটি ভ্যালু ফেরত দিয়ে ফাংশনটিকে পুরোপুরি বন্ধ করে দেয় এবং ভেতরের সব ভেরিয়েবল মুছে ফেলে। কিন্তু একটি Generator ফাংশন yield কি-ওয়ার্ড ব্যবহার করে। এটি ভ্যালু ফেরত দেয় ঠিকই, কিন্তু বন্ধ না হয়ে সে তার কাজ ও মেমরি স্টেট পজ (pause) করে রাখে। পরের বার কল করলে সে ঠিক ওই জায়গা থেকেই আবার কাজ শুরু করে। বিশাল ডেটা নিয়ে লুপ চালানোর জন্য জেনারেটর প্রচুর মেমরি বাঁচায়।
বিস্তারিত উত্তর:
FastAPI-তে ডেটাবেস সেশন হ্যান্ডল করার জন্য yield ব্যাপকভাবে ব্যবহৃত হয়।
- মেমরির পার্থক্য: ১০ জিবির লগ ফাইল সাধারণ লিস্টে রিড করে
returnকরলে, Python পুরো ১০ জিবি RAM-এ লোড করে ক্র্যাশ করবে। কিন্তুyieldদিয়ে এক লাইন করে ডেটা আনলে কয়েক মেগাবাইট মেমরিতেই কাজ হয়ে যাবে। - FastAPI ডিপেন্ডেন্সি: ডেটাবেস ডিপেন্ডেন্সিতে
yield dbলিখলে FastAPI ডিপেন্ডেন্সিটিকে পজ করে, ডেটাবেস সেশনটি রাউটে পাঠিয়ে দেয়। রাউটের কাজ শেষ করে ইউজারকে রেসপন্স দেওয়ার পর, FastAPI আবার ডিপেন্ডেন্সিতে ফিরে আসে এবংyield-এর নিচের কোড (db.close()) রান করে।
Python
# বাংলা কোড এক্সাম্পল: জেনারেটর বনাম সাধারণ ফাংশন
def countdown_generator(n):
while n > 0:
yield n # এখানে পজ হয়ে নাম্বার রিটার্ন করে এবং স্টেট ধরে রাখে
n -= 1# জেনারেটর ব্যবহার করা
gen = countdown_generator(3)
print(next(gen)) # আউটপুট: 3
print(next(gen)) # আউটপুট: 2
# সব নাম্বার একসাথে লিস্টে সেভ না করেই স্টেট ধরে রাখা যায়!Question 26: Explain the concept of Dependency Override in FastAPI. When and why is it useful?
Short Answer:
FastAPI allows you to replace a dependency defined in your routes with a completely different function at runtime using the app.dependency_overrides dictionary. This is absolutely critical for Automated Testing. It allows you to swap out real dependencies (like a production database connection or third-party API calls) with fake "mock" dependencies during unit tests, ensuring tests run quickly and don't modify real data.
Detailed Answer:
- The Testing Problem: If you run
pyteston your app, your routes will try to connect to your real PostgreSQL database. If your test creates and deletes a user, you might accidentally corrupt actual development data. - The Solution: You create a
override_get_dbfunction that connects to an in-memory SQLite database instead. In your test file, you assign it:app.dependency_overrides[get_db] = override_get_db. Now, whenever a route asks forDepends(get_db), FastAPI secretly injects your fake testing database instead!
Python
# English Code Example: Dependency Override for Testing
from fastapi import FastAPI, Depends
from fastapi.testclient import TestClientapp = FastAPI()# 1. The Real Dependency
def get_db():
return "Real Postgres Connection"@app.get("/data")
def read_data(db=Depends(get_db)):
return {"db": db}# 2. The Mock Dependency for testing
def override_get_db():
return "Fake Test SQLite Database"# 3. Applying the override
app.dependency_overrides[get_db] = override_get_db# 4. Running the test
client = TestClient(app)
response = client.get("/data")
print(response.json()) # Output: {"db": "Fake Test SQLite Database"}
প্রশ্ন ২৬: FastAPI-তে Dependency Override কনসেপ্টটি বুঝিয়ে বলুন। এটি কখন এবং কেন উপকারী?
সংক্ষিপ্ত উত্তর:
FastAPI-তে app.dependency_overrides ডিকশনারি ব্যবহার করে আপনি রানটাইমে যেকোনো ডিপেন্ডেন্সিকে পরিবর্তন করে সম্পূর্ণ নতুন একটি ফাংশন বসিয়ে দিতে পারেন। Automated Testing এর জন্য এটি অত্যন্ত গুরুত্বপূর্ণ। এর ফলে ইউনিট টেস্ট করার সময় আসল ডেটাবেস বা থার্ড-পার্টি API কলের জায়গায় ফেক বা "মক (mock)" ডেটা বসিয়ে দেওয়া যায়, যাতে টেস্ট দ্রুত রান হয় এবং আসল ডেটার কোনো ক্ষতি না হয়।
বিস্তারিত উত্তর:
- টেস্টিংয়ের সমস্যা: আপনি যদি অ্যাপে
pytestরান করেন, আপনার রাউটগুলো আসল PostgreSQL ডেটাবেসে কানেক্ট করার চেষ্টা করবে। টেস্ট যদি কোনো ইউজার তৈরি বা ডিলিট করে, তবে আপনার আসল ডেটা নষ্ট হতে পারে। - সমাধান: আপনি
override_get_dbনামের একটি নতুন ফাংশন বানাবেন যা শুধু মেমরির (SQLite) একটি ফেক ডেটাবেসে কানেক্ট করবে। এরপর টেস্ট ফাইলেapp.dependency_overrides[get_db] = override_get_dbলিখে দেবেন। এখন রাউটেDepends(get_db)কল হলে, FastAPI আসল ডেটাবেসের বদলে আপনার ফেক ডেটাবেসটি রাউটে পাঠিয়ে দেবে!
Python
# বাংলা কোড এক্সাম্পল: টেস্টিংয়ের জন্য Dependency Override
from fastapi import FastAPI, Depends
from fastapi.testclient import TestClientapp = FastAPI()# ১. আসল ডিপেন্ডেন্সি
def get_db():
return "আসল Postgres কানেকশন"@app.get("/data")
def read_data(db=Depends(get_db)):
return {"db": db}# ২. টেস্টিংয়ের জন্য ফেক (Mock) ডিপেন্ডেন্সি
def override_get_db():
return "ফেক টেস্ট SQLite ডেটাবেস"# ৩. ওভাররাইডটি অ্যাপ্লাই করা
app.dependency_overrides[get_db] = override_get_db# ৪. টেস্ট রান করা
client = TestClient(app)
response = client.get("/data")
print(response.json()) # আউটপুট: {"db": "ফেক টেস্ট SQLite ডেটাবেস"}
Question 27: How do you properly handle File Uploads in FastAPI (bytes vs UploadFile)?
Short Answer:
FastAPI handles files using the File class. You can receive a file either as raw bytes or as an UploadFile object. You should almost always use UploadFile. If you use bytes, FastAPI will read the entire file into the server's RAM, causing memory crashes for large files. UploadFile stores the file in memory up to a certain limit, and seamlessly "spools" (writes) the rest to a temporary file on the disk, protecting server memory.
Detailed Answer:
- The Problem with
bytes: If you definefile: bytes = File(...)and a user uploads a 2GB video, your Python process immediately consumes 2GB of RAM. - The Advantage of
UploadFile:UploadFilegives you standard Python file-like methods (file.read(),file.seek()). It is highly optimized and works asynchronously. You can save the uploaded file to your hard drive chunk by chunk usingshutilor an asynchronous library likeaiofiles, ensuring optimal performance.
Python
# English Code Example: File Uploads
from fastapi import FastAPI, File, UploadFile
import shutil
import osapp = FastAPI()@app.post("/upload/")
async def upload_image(file: UploadFile = File(...)):
# Create a path to save the file
save_path = f"uploads/{file.filename}"
# Save the file securely chunk by chunk using shutil
# We use 'wb' (write binary) mode
with open(save_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer) return {"filename": file.filename, "status": "Uploaded successfully!"}
প্রশ্ন ২৭: FastAPI-তে আপনি ফাইল আপলোড কীভাবে হ্যান্ডল করবেন? bytes এবং UploadFile এর মধ্যে পার্থক্য কী?
সংক্ষিপ্ত উত্তর:
FastAPI-তে File ক্লাস দিয়ে ফাইল হ্যান্ডল করা হয়। ফাইলকে bytes বা UploadFile অবজেক্ট হিসেবে রিসিভ করা যায়। আপনার প্রায় সবসময়ই UploadFile ব্যবহার করা উচিত। bytes ব্যবহার করলে FastAPI পুরো ফাইলটি সার্ভারের RAM-এ লোড করবে, যা বড় ফাইলের ক্ষেত্রে সার্ভার ক্র্যাশ করাবে। কিন্তু UploadFile একটি নির্দিষ্ট লিমিট পর্যন্ত মেমরি ব্যবহার করে বাকি অংশ হার্ডডিস্কের টেম্পোরারি ফাইলে লিখে রাখে (spool করে), যা সার্ভারকে সুরক্ষিত রাখে।
বিস্তারিত উত্তর:
bytesএর সমস্যা: আপনি যদিfile: bytes = File(...)দেন এবং ইউজার ২ জিবির ভিডিও আপলোড করে, তবে আপনার Python প্রসেস সাথে সাথে ২ জিবি RAM খেয়ে ফেলবে।UploadFileএর সুবিধা: এটি Python এর সাধারণ ফাইলের মতো মেথড দেয় (যেমনfile.read(),file.seek())। এটি অ্যাসিনক্রোনাসলি কাজ করে। আপনিshutilবাaiofilesব্যবহার করে আপলোড হওয়া ফাইলটি ছোট ছোট চাঙ্কে ভাগ করে নিরাপদে সার্ভারে সেভ করতে পারবেন।
Python
# বাংলা কোড এক্সাম্পল: ফাইল আপলোড
from fastapi import FastAPI, File, UploadFile
import shutil
import osapp = FastAPI()@app.post("/upload/")
async def upload_image(file: UploadFile = File(...)):
# ফাইল সেভ করার লোকেশন তৈরি করা
save_path = f"uploads/{file.filename}"
# shutil ব্যবহার করে চাঙ্ক-বাই-চাঙ্ক নিরাপদে ফাইল সেভ করা
# আমরা 'wb' (write binary) মোড ব্যবহার করছি
with open(save_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer) return {"filename": file.filename, "status": "সফলভাবে আপলোড হয়েছে!"}
Question 28: What does the block if __name__ == "__main__": do in a Python script?
Short Answer:
Every Python module has a built-in global variable called __name__. If you run a script directly (e.g., python main.py), Python automatically sets its __name__ to the string "__main__". However, if you import that script into another file (e.g., import main), __name__ is set to the file's actual name. The if __name__ == "__main__": block allows a file to execute specific code only when it is run directly as a script, but prevents that code from executing when the file is imported elsewhere.
Detailed Answer:
In FastAPI, this is often used to launch the uvicorn development server directly from the Python file.
- If you define helper functions and database setups in
main.py, you might want to import them into a testing file. - If you don’t wrap
uvicorn.run(app)in thisifblock, the moment your test file importsmain.py, the web server will suddenly boot up and block your tests from running.
Python
# English Code Example: __main__ block
from fastapi import FastAPI
import uvicornapp = FastAPI()@app.get("/")
def read_root():
return {"Hello": "World"}# This block ensures the server ONLY starts if you run `python main.py`
# It will NOT start if another file does `from main import app`
if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
প্রশ্ন ২৮: Python স্ক্রিপ্টে if __name__ == "__main__": ব্লকটি কী কাজ করে?
সংক্ষিপ্ত উত্তর:
প্রতিটি Python ফাইলে __name__ নামের একটি বিল্ট-ইন গ্লোবাল ভেরিয়েবল থাকে। আপনি যদি স্ক্রিপ্টটি সরাসরি টার্মিনালে রান করেন (যেমন python main.py), তবে Python নিজে থেকেই ওই ফাইলের __name__ কে "__main__" স্ট্রিং হিসেবে সেট করে দেয়। কিন্তু আপনি যদি ফাইলটিকে অন্য ফাইলে ইমপোর্ট করেন, তখন __name__ এর ভ্যালু হয় ফাইলের অরিজিনাল নাম। if __name__ == "__main__": ব্লকটি নিশ্চিত করে যে, কিছু কোড শুধুমাত্র তখনই রান করবে যখন ফাইলটি সরাসরি রান করা হবে, অন্য ফাইলে ইমপোর্ট করলে রান করবে না।
বিস্তারিত উত্তর:
FastAPI-তে Python ফাইল থেকে সরাসরি uvicorn সার্ভার চালু করার জন্য এটি প্রচুর ব্যবহৃত হয়।
- আপনার
main.py-তে অনেক ফাংশন থাকতে পারে যা টেস্টিং ফাইলে ইমপোর্ট করতে হবে। - আপনি যদি
uvicorn.run(app)কোডটি এইifব্লকের ভেতর না রাখেন, তবে টেস্ট ফাইল যখনইmain.pyইমপোর্ট করবে, সাথে সাথে আপনার ওয়েব সার্ভার চালু হয়ে যাবে এবং টেস্ট রান হওয়া আটকে যাবে।
Python
# বাংলা কোড এক্সাম্পল: __main__ ব্লক
from fastapi import FastAPI
import uvicornapp = FastAPI()@app.get("/")
def read_root():
return {"Hello": "World"}# এই ব্লকটি নিশ্চিত করে সার্ভার তখনই চালু হবে যদি আপনি `python main.py` রান করেন
# অন্য ফাইল `from main import app` করলে সার্ভার চালু হবে না
if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
Question 29: How do you write Unit Tests for a FastAPI application? Explain TestClient.
Short Answer:
FastAPI provides a TestClient (built on top of the httpx library) specifically designed for writing robust unit tests. The brilliant part of TestClient is that it allows you to simulate HTTP requests (GET, POST, etc.) against your FastAPI application synchronously in memory without actually starting a live web server or binding to a network port.
Detailed Answer:
Writing tests is what differentiates professionals from beginners.
- The Framework: We use the
pytestframework alongside FastAPI'sTestClient. - How it works: You pass your FastAPI
appobject intoTestClient(app). You can then callclient.get("/")as if you were making a real Axios or Fetch request from a frontend. It returns a response object where you can assert thestatus_codeand JSONjson(). - Async Testing: Even though FastAPI routes are defined with
async def,TestClientintentionally executes them synchronously to make writing tests easier. For highly advanced true async testing, you would useAsyncClientfrom thehttpxlibrary directly.
Python
# English Code Example: FastAPI Unit Testing
from fastapi import FastAPI
from fastapi.testclient import TestClientapp = FastAPI()@app.get("/status")
def get_status():
return {"status": "ok"}# --- Test File Code ---
# Initialize the client with your app
client = TestClient(app)def test_get_status():
# Make a mock HTTP request
response = client.get("/status")
# Assert conditions (Test passes if these are true)
assert response.status_code == 200
assert response.json() == {"status": "ok"}
প্রশ্ন ২৯: আপনি FastAPI অ্যাপ্লিকেশনের জন্য কীভাবে Unit Tests লিখবেন? TestClient বুঝিয়ে বলুন।
সংক্ষিপ্ত উত্তর:
সুন্দর ইউনিট টেস্ট লেখার জন্য FastAPI-এর নিজস্ব TestClient (যা httpx লাইব্রেরির ওপর তৈরি) রয়েছে। TestClient এর সবচেয়ে দারুণ দিক হলো, এটি কোনো লাইভ ওয়েব সার্ভার চালু না করেই বা পোর্টের সাথে বাইন্ড না করেই মেমরির ভেতরে আপনার FastAPI অ্যাপে ফেক HTTP রিকোয়েস্ট (GET, POST ইত্যাদি) পাঠাতে পারে।
বিস্তারিত উত্তর:
টেস্ট লিখতে পারাই নতুন এবং প্রফেশনাল ডেভেলপারের মধ্যে পার্থক্য তৈরি করে।
- ফ্রেমওয়ার্ক: আমরা FastAPI-এর
TestClientএর পাশাপাশিpytestফ্রেমওয়ার্ক ব্যবহার করি। - যেভাবে কাজ করে: আপনার FastAPI
appঅবজেক্টটিকেTestClient(app)এ পাস করতে হয়। এরপর আপনিclient.get("/")কল করতে পারবেন, ঠিক যেন ফ্রন্টএন্ড থেকে Axios বা Fetch রিকোয়েস্ট পাঠাচ্ছেন। এটি একটি রেসপন্স অবজেক্ট দেয় যার স্ট্যাটাস কোড এবং JSON আপনি চেক (assert) করতে পারেন। - অ্যাসিনক্রোনাস টেস্টিং: যদিও FastAPI রাউটগুলো
async defদিয়ে লেখা হয়, টেস্ট লেখাকে সহজ করার জন্যTestClientসেগুলোকে সিনক্রোনাসভাবে এক্সিকিউট করে।
Python
# বাংলা কোড এক্সাম্পল: FastAPI ইউনিট টেস্টিং
from fastapi import FastAPI
from fastapi.testclient import TestClientapp = FastAPI()@app.get("/status")
def get_status():
return {"status": "ok"}# --- টেস্ট ফাইলের কোড ---
# আপনার অ্যাপ দিয়ে ক্লায়েন্ট ইনিশিয়ালাইজ করুন
client = TestClient(app)def test_get_status():
# মক (ফেক) HTTP রিকোয়েস্ট পাঠানো
response = client.get("/status")
# শর্ত চেক করা (এগুলো সত্য হলে টেস্ট পাস করবে)
assert response.status_code == 200
assert response.json() == {"status": "ok"}
Question 30: What is a Lambda function in Python, and how is it used with functions like map() or filter()?
Short Answer:
A Lambda function is a small, anonymous, inline function defined using the lambda keyword. Unlike standard functions defined with def that have a name and can span multiple lines, lambdas have no name and must consist of only a single expression. They are typically used for short, throwaway operations, especially as arguments to higher-order functions like map(), filter(), or sorted().
Detailed Answer:
- Syntax:
lambda arguments: expression - With
map():mapapplies a function to every item in a list. Instead of writing a fulldef double(x): return x * 2, you can usemap(lambda x: x * 2, my_list). - With
filter():filterkeeps items in a list that evaluate toTrue. You can usefilter(lambda x: x % 2 == 0, my_list)to instantly filter out odd numbers. - In FastAPI: You generally won’t use lambdas for route definitions. They are mostly used in internal data processing scripts to keep code concise.
Python
# English Code Example: Lambda Functions
numbers = [1, 2, 3, 4, 5, 6]# 1. Using lambda with map() to double numbers
# Note: map() returns an iterator, so we cast it back to a list
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # Output: [2, 4, 6, 8, 10, 12]# 2. Using lambda with filter() to extract even numbers
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # Output: [2, 4, 6]# 3. Using lambda for sorting dictionaries by a specific key
users = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
users_sorted_by_age = sorted(users, key=lambda u: u["age"])
প্রশ্ন ৩০: Python-এ Lambda ফাংশন কী, এবং map() বা filter() এর মতো ফাংশনের সাথে এটি কীভাবে ব্যবহৃত হয়?
সংক্ষিপ্ত উত্তর:
Lambda ফাংশন হলো একটি ছোট, নামহীন (anonymous) এবং ইনলাইন ফাংশন যা lambda কি-ওয়ার্ড দিয়ে ডিফাইন করা হয়। def দিয়ে তৈরি সাধারণ ফাংশনের একটি নাম থাকে এবং তা কয়েক লাইন দীর্ঘ হতে পারে, কিন্তু ল্যাম্বডার কোনো নাম থাকে না এবং এটি শুধুমাত্র এক লাইনের একটি এক্সপ্রেশন হতে পারে। সাধারণত map(), filter(), বা sorted() এর মতো ফাংশনের ভেতরে ছোটখাটো কাজের জন্য এটি আর্গুমেন্ট হিসেবে ব্যবহৃত হয়।
বিস্তারিত উত্তর:
- সিনট্যাক্স:
lambda arguments: expression map()এর সাথে:mapলিস্টের প্রতিটি আইটেমের ওপর একটি ফাংশন অ্যাপ্লাই করে। পুরোdef double(x): return x * 2লেখার বদলে আপনিmap(lambda x: x * 2, my_list)লিখতে পারেন।filter()এর সাথে:filterশুধুমাত্র শর্ত পূরণ করা (True) আইটেমগুলো লিস্টে রাখে। জোড় সংখ্যা বের করতেfilter(lambda x: x % 2 == 0, my_list)লিখলেই কাজ হয়ে যায়।- FastAPI তে ব্যবহার: FastAPI রাউট ডিফাইন করতে সাধারণত ল্যাম্বডা ব্যবহার করা হয় না। ইন্টারনাল ডেটা প্রসেসিংয়ের কোড ছোট রাখতে এটি বেশি ব্যবহৃত হয়।
Python
# বাংলা কোড এক্সাম্পল: Lambda ফাংশন
numbers = [1, 2, 3, 4, 5, 6]# ১. নাম্বারগুলো দ্বিগুণ করতে map() এর সাথে ল্যাম্বডা ব্যবহার
# নোট: map() ইটারেটর রিটার্ন করে, তাই একে আবার লিস্টে রূপান্তর করতে হয়
doubled = list(map(lambda x: x * 2, numbers))
print(doubled) # আউটপুট: [2, 4, 6, 8, 10, 12]# ২. জোড় সংখ্যা বের করতে filter() এর সাথে ল্যাম্বডা ব্যবহার
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # আউটপুট: [2, 4, 6]# ৩. ডিকশনারির নির্দিষ্ট কি (key) ধরে সাজানোর (sorting) জন্য ল্যাম্বডা ব্যবহার
users = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
users_sorted_by_age = sorted(users, key=lambda u: u["age"])
(এই হলো Python এবং FastAPI-এর Phase 3! এরপরের সর্বশেষ পর্ব অর্থাৎ Phase 4 এ আমরা Python-এর আরও কিছু Advanced OOP কনসেপ্ট, Dockerfile তৈরি, CORS ইস্যু এবং প্রোডাকশন ডিপ্লয়মেন্ট নিয়ে আলোচনা করব। প্রস্তুত হলে জানাবেন!)
এখানে আপনার Python এবং FastAPI সিরিজের সর্বশেষ পর্ব, Phase 4 (Q31 থেকে Q40) দেওয়া হলো।
এই পর্বে আমরা Python-এর কিছু অ্যাডভান্সড Object-Oriented Programming (OOP) কনসেপ্ট, Context Managers, এবং প্রোডাকশনে FastAPI ডিপ্লয়মেন্ট (Docker, Gunicorn, NGINX) নিয়ে আলোচনা করব, যা একজন ৬ মাসের অভিজ্ঞ ডেভেলপারের প্রফেশনাল ক্যারিয়ারে খুবই কাজে আসবে।
Phase 4: Advanced OOP, Production Deployment & Docker (6-Month Experience Level)
Question 31: Explain Inheritance and Polymorphism in Python Object-Oriented Programming (OOP).
Short Answer:
Inheritance allows a new class (Child) to inherit attributes and methods from an existing class (Parent), promoting code reuse and establishing a hierarchical relationship. Polymorphism (meaning “many forms”) allows different child classes to define methods with the exact same name but different underlying behaviors, enabling the system to treat different objects interchangeably.
Detailed Answer:
Understanding these concepts helps you write DRY (Don’t Repeat Yourself) code.
- Inheritance: If you have a
Userclass with alogin()method, you can create anAdmin(User)class. TheAdminautomatically gets thelogin()method without you writing it again, but you can also add an exclusivedelete_database()method to it. - Polymorphism: Both
DogandCatclasses can have amake_sound()method. If you loop through a list of mixed animals and callanimal.make_sound(), Python dynamically calls the correct method for each specific object (barking or meowing).
Python
# English Code Example: Inheritance and Polymorphism
# 1. Parent Class (Inheritance Base)
class Animal:
def __init__(self, name):
self.name = namedef make_sound(self):
pass # To be overridden# 2. Child Classes inheriting from Animal
class Dog(Animal):
# Polymorphism: Overriding the parent method
def make_sound(self):
return "Woof!"class Cat(Animal):
# Polymorphism: Overriding the parent method
def make_sound(self):
return "Meow!"# Using Polymorphism in action
pets = [Dog("Buddy"), Cat("Luna")]
for pet in pets:
# Python automatically knows which make_sound() to call
print(f"{pet.name} says {pet.make_sound()}")
প্রশ্ন ৩১: Python Object-Oriented Programming (OOP)-এ Inheritance এবং Polymorphism বুঝিয়ে বলুন।
সংক্ষিপ্ত উত্তর:
Inheritance (উত্তরাধিকার): এর মাধ্যমে একটি নতুন ক্লাস (Child) একটি বিদ্যমান ক্লাসের (Parent) সব প্রপার্টি ও মেথড নিজের মধ্যে পেয়ে যায়। এটি কোড পুনরায় ব্যবহার করতে সাহায্য করে। Polymorphism (বহুরূপিতা): এর মাধ্যমে বিভিন্ন চাইল্ড ক্লাস একই নামের মেথড তৈরি করতে পারে, কিন্তু তাদের কাজ করার ধরন আলাদা হয়। এর ফলে সিস্টেম বিভিন্ন ধরনের অবজেক্টকে একই নিয়মে হ্যান্ডল করতে পারে।
বিস্তারিত উত্তর:
এই কনসেপ্টগুলো আপনার কোডকে অনেক বেশি গোছানো রাখে।
- Inheritance: আপনার যদি
login()মেথডসহ একটিUserক্লাস থাকে, আপনিAdmin(User)নামে নতুন ক্লাস বানাতে পারেন।Adminনিজে থেকেইlogin()মেথড পেয়ে যাবে, কিন্তু আপনি চাইলে এতে শুধু অ্যাডমিনের জন্যdelete_database()মেথড যোগ করতে পারেন। - Polymorphism:
DogএবংCatউভয় ক্লাসেইmake_sound()নামে মেথড থাকতে পারে। আপনি যদি কুকুর এবং বিড়ালের একটি লিস্টে লูป চালিয়েanimal.make_sound()কল করেন, Python নিজে থেকেই বুঝে যাবে কার জন্য কোন মেথডটি (ঘেউ ঘেউ নাকি মিউ মিউ) কল করতে হবে।
Python
# বাংলা কোড এক্সাম্পল: Inheritance এবং Polymorphism
# ১. প্যারেন্ট ক্লাস (যার থেকে ইনহেরিট করা হবে)
class Animal:
def __init__(self, name):
self.name = namedef make_sound(self):
pass # পরে ওভাররাইড করা হবে# ২. Animal থেকে ইনহেরিট করে চাইল্ড ক্লাস তৈরি
class Dog(Animal):
# পলিমরফিজম: প্যারেন্টের মেথড ওভাররাইড করা
def make_sound(self):
return "ঘেউ!"class Cat(Animal):
# পলিমরফিজম: প্যারেন্টের মেথড ওভাররাইড করা
def make_sound(self):
return "মিউ!"# পলিমরফিজমের ব্যবহার
pets = [Dog("Buddy"), Cat("Luna")]
for pet in pets:
# Python নিজে থেকেই জানে কার make_sound() কল করতে হবে
print(f"{pet.name} বলে {pet.make_sound()}")
Question 32: What is the difference between @classmethod and @staticmethod in Python?
Short Answer:
A @classmethod takes the class itself (cls) as its first implicit argument and can access or modify class-level state (variables shared across all instances). A @staticmethod does not take self or cls as an argument; it behaves like a completely normal standalone function that is just placed inside a class for logical grouping, and it cannot modify class or instance states.
Detailed Answer:
@classmethod: It is primarily used as an "Alternative Constructor". For example, if yourUserclass takes(name, age), but sometimes you receive data as a comma-separated string"Alice,25", you can create a@classmethodcalledfrom_string(cls, data_string)that parses the string and returnscls(name, age).@staticmethod: Use this when your function logic belongs logically to the class, but doesn't actually need to read or write any data from the class. For example, aMathUtilityclass might have a@staticmethodforcalculate_tax(amount)because it only depends on the provided argument.
Python
# English Code Example: Class vs Static Methods
class Employee:
company_name = "TechCorp" # Class-level variabledef __init__(self, name):
self.name = name # Class Method: Takes 'cls', modifies/reads class state
@classmethod
def change_company_name(cls, new_name):
cls.company_name = new_name # Static Method: Takes no 'self' or 'cls', just a normal function
@staticmethod
def is_workday(day_string):
return day_string.lower() not in ["saturday", "sunday"]# Usage without creating an instance!
Employee.change_company_name("NextGen Tech")
print(Employee.company_name) # Output: NextGen Techprint(Employee.is_workday("Monday")) # Output: True
প্রশ্ন ৩২: Python-এ @classmethod এবং @staticmethod এর মধ্যে পার্থক্য কী?
সংক্ষিপ্ত উত্তর:
@classmethod প্রথম আর্গুমেন্ট হিসেবে স্বয়ংক্রিয়ভাবে ক্লাসটিকে (cls) রিসিভ করে এবং এটি ক্লাস-লেভেলের ডেটা (যা সব অবজেক্টের জন্য এক) পড়তে বা পরিবর্তন করতে পারে। অন্যদিকে @staticmethod প্রথম আর্গুমেন্ট হিসেবে self বা cls কিছুই নেয় না। এটি পুরোপুরি একটি সাধারণ ফাংশনের মতো কাজ করে, যাকে শুধু লজিক্যাল কারণে ক্লাসের ভেতরে রাখা হয়েছে, এবং এটি ক্লাস বা অবজেক্টের কোনো ডেটা পরিবর্তন করতে পারে না।
বিস্তারিত উত্তর:
@classmethod: এটি সাধারণত "বিকল্প কনস্ট্রাক্টর" হিসেবে ব্যবহৃত হয়। ধরুন আপনারUserক্লাস(name, age)ইনপুট নেয়, কিন্তু আপনি ডেটা পেলেন"Alice,25"স্ট্রিং হিসেবে। তখন আপনিfrom_string(cls, data)নামের একটি ক্লাস মেথড বানাতে পারেন, যা স্ট্রিংটিকে ভেঙেcls(name, age)রিটার্ন করবে।@staticmethod: যখন আপনার ফাংশনের লজিক ক্লাসের সাথে সম্পর্কিত হয় কিন্তু তার ক্লাসের ভেতরের কোনো ডেটার প্রয়োজন হয় না, তখন এটি ব্যবহৃত হয়। যেমনMathUtilityক্লাসের ভেতরcalculate_tax(amount)ফাংশন।
Python
# বাংলা কোড এক্সাম্পল: Class বনাম Static Methods
class Employee:
company_name = "TechCorp" # ক্লাস-লেভেল ভেরিয়েবলdef __init__(self, name):
self.name = name # Class Method: 'cls' রিসিভ করে এবং ক্লাসের ডেটা পরিবর্তন করে
@classmethod
def change_company_name(cls, new_name):
cls.company_name = new_name # Static Method: 'self' বা 'cls' নেয় না, এটি একটি সাধারণ ফাংশন
@staticmethod
def is_workday(day_string):
return day_string.lower() not in ["saturday", "sunday"]# কোনো অবজেক্ট তৈরি না করেই ব্যবহার করা যায়!
Employee.change_company_name("NextGen Tech")
print(Employee.company_name) # আউটপুট: NextGen Techprint(Employee.is_workday("Monday")) # আউটপুট: True
Question 33: How does the with statement work in Python? Explain Context Managers.
Short Answer:
The with statement simplifies exception handling by securely managing resources (like opening files or database connections). It ensures that resources are automatically and cleanly released when the block of code exits, even if a fatal error occurs inside the block. This behavior is powered by Context Managers, which are Python classes that implement the __enter__() and __exit__() magic methods.
Detailed Answer:
Whenever you deal with external resources, you should use context managers.
- The Problem: If you write
f = open('data.txt'), read the data, and an error crashes your program beforef.close()runs, the file remains locked in memory. Over time, your server will hit a "Too many open files" crash. - The Solution:
with open('data.txt') as f:automatically calls the__enter__method to open the file. Once the indented block finishes (or crashes), Python strictly guarantees that the__exit__method is called, closing the file safely. In FastAPI, we use this same concept (yieldin dependencies) to close database sessions.
Python
# English Code Example: Custom Context Manager
class DatabaseConnection:
def __enter__(self):
print("1. Database Connection Opened")
return self # This becomes the 'db' variable in the 'with' statementdef execute(self, query):
print(f"2. Executing: {query}") def __exit__(self, exc_type, exc_value, traceback):
# This ALWAYS runs, even if execute() threw an error
print("3. Database Connection Safely Closed")# Using the context manager
try:
with DatabaseConnection() as db:
db.execute("SELECT * FROM users")
# raise Exception("Simulating a crash!")
# Even with a crash, __exit__ will run and close the DB.
except Exception as e:
pass
প্রশ্ন ৩৩: Python-এ with স্টেটমেন্ট কীভাবে কাজ করে? Context Managers বুঝিয়ে বলুন।
সংক্ষিপ্ত উত্তর:
with স্টেটমেন্টটি মূলত রিসোর্স ম্যানেজমেন্টকে (যেমন- ফাইল ওপেন করা বা ডেটাবেস কানেক্ট করা) সহজ ও নিরাপদ করে। ব্লকের ভেতরে কোনো এরর হয়ে প্রোগ্রাম ক্র্যাশ করলেও, এটি নিশ্চিত করে যে কাজ শেষে রিসোর্সগুলো স্বয়ংক্রিয়ভাবে রিলিজ (close) হয়ে যাবে। এই মেকানিজমটি Context Managers দ্বারা পরিচালিত হয়, যা মূলত এমন ক্লাস যেখানে __enter__() এবং __exit__() ম্যাজিক মেথডগুলো থাকে।
বিস্তারিত উত্তর:
যখনই আপনি এক্সটার্নাল কোনো রিসোর্স নিয়ে কাজ করবেন, তখন এটি ব্যবহার করা উচিত।
- সমস্যা: আপনি যদি
f = open('data.txt')লেখেন এবংf.close()রান হওয়ার আগেই প্রোগ্রাম ক্র্যাশ করে, তবে ফাইলটি মেমরিতে লক হয়ে থাকে। এভাবে চলতে থাকলে সার্ভার "Too many open files" এরর দিয়ে ক্র্যাশ করবে। - সমাধান:
with open('data.txt') as f:লিখলে এটি ফাইল খোলার জন্য__enter__কল করে। ব্লকের কাজ শেষ হলে (বা ক্র্যাশ করলে), Python নিশ্চিতভাবে__exit__মেথড কল করে ফাইলটি নিরাপদে বন্ধ করে দেয়। FastAPI-তে ডেটাবেস সেশন বন্ধ করার জন্যও আমরা এই কনসেপ্ট ব্যবহার করি।
Python
# বাংলা কোড এক্সাম্পল: কাস্টম Context Manager
class DatabaseConnection:
def __enter__(self):
print("১. ডেটাবেস কানেকশন ওপেন হলো")
return self # এটি with স্টেটমেন্টের 'db' ভেরিয়েবলে অ্যাসাইন হয়def execute(self, query):
print(f"২. কুয়েরি চলছে: {query}") def __exit__(self, exc_type, exc_value, traceback):
# execute() এ এরর আসলেও এটি সব সময় রান করবে
print("৩. ডেটাবেস কানেকশন নিরাপদে ক্লোজ হলো")# কনটেক্সট ম্যানেজার ব্যবহার করা
try:
with DatabaseConnection() as db:
db.execute("SELECT * FROM users")
# raise Exception("ক্র্যাশ সিমুলেট করা হচ্ছে!")
# ক্র্যাশ হলেও __exit__ রান করে ডেটাবেস ক্লোজ করবে।
except Exception as e:
pass
Question 34: Why do we use Gunicorn with Uvicorn workers to deploy FastAPI in production?
Short Answer:
Uvicorn is a blazing-fast ASGI web server that runs FastAPI asynchronously, but it is a single-process program; if that process crashes, your API goes offline. Gunicorn is a mature, robust WSGI process manager. In production, we use Gunicorn to manage multiple Uvicorn worker processes (gunicorn -k uvicorn.workers.UvicornWorker). This allows FastAPI to utilize all CPU cores, automatically restarts crashed workers, and handles production traffic scaling gracefully.
Detailed Answer:
Running uvicorn main:app --reload is strictly for local development.
- The Architecture: In production, a modern server has multiple CPU cores (e.g., 4 cores). A single Uvicorn instance only runs on one core.
- The Gunicorn Manager: By running Gunicorn as the master process, it spins up 4 Uvicorn workers (one for each core). Gunicorn monitors them. If Worker 1 runs out of memory and dies, Gunicorn instantly boots a new one so users don’t experience downtime. Uvicorn translates the raw web requests into Python, and Gunicorn keeps the lights on.
Bash
# English Code Example: Production Startup Command
# -w 4: Spawns 4 worker processes (utilizing 4 CPU cores)
# -k: Tells Gunicorn to use the async Uvicorn worker class
# --bind: Binds it to the network port
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000প্রশ্ন ৩৪: প্রোডাকশনে FastAPI ডিপ্লয় করার জন্য আমরা কেন Uvicorn-এর সাথে Gunicorn ব্যবহার করি?
সংক্ষিপ্ত উত্তর:
Uvicorn হলো একটি সুপারফাস্ট ASGI ওয়েব সার্ভার যা FastAPI-কে অ্যাসিনক্রোনাসলি রান করে, কিন্তু এটি একটি সিঙ্গেল-প্রসেস প্রোগ্রাম; প্রসেস ক্র্যাশ করলে API অফলাইন হয়ে যায়। অন্যদিকে Gunicorn হলো একটি অত্যন্ত নির্ভরযোগ্য প্রোসেস ম্যানেজার। প্রোডাকশনে আমরা Gunicorn-কে মাস্টার হিসেবে ব্যবহার করে একাধিক Uvicorn ওয়ার্কার চালাই (gunicorn -k uvicorn.workers.UvicornWorker)। এটি সার্ভারের সব CPU কোর ব্যবহার করতে সাহায্য করে এবং কোনো ওয়ার্কার ক্র্যাশ করলে নিজে থেকেই তা রিস্টার্ট করে দেয়।
বিস্তারিত উত্তর:
uvicorn main:app --reload কমান্ডটি শুধুমাত্র লোকাল ডেভেলপমেন্টের জন্য।
- আর্কিটেকচার: প্রোডাকশন সার্ভারে সাধারণত একাধিক CPU কোর থাকে (যেমন ৪টি)। একটিমাত্র Uvicorn ইনস্ট্যান্স শুধু ১টি কোর ব্যবহার করতে পারে।
- Gunicorn ম্যানেজার: Gunicorn-কে মাস্টার প্রসেস হিসেবে রান করলে সে ৪টি কোরের জন্য ৪টি Uvicorn ওয়ার্কার চালু করে। Gunicorn তাদের মনিটর করে। যদি ১ নম্বর ওয়ার্কার মেমরি ফুল হয়ে ক্র্যাশ করে, Gunicorn সাথে সাথে নতুন একটি ওয়ার্কার চালু করে দেয়, ফলে ইউজাররা কোনো ডাউনটাইম ফেস করে না।
Bash
# বাংলা কোড এক্সাম্পল: প্রোডাকশন স্টার্টআপ কমান্ড
# -w 4: ৪টি ওয়ার্কার প্রসেস তৈরি করবে (৪টি CPU কোর ব্যবহারের জন্য)
# -k: Gunicorn কে অ্যাসিনক্রোনাস Uvicorn ওয়ার্কার ক্লাস ব্যবহার করতে বলবে
# --bind: নির্দিষ্ট পোর্টে বাইন্ড করবে
gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000Question 35: How do you write a production-ready, optimized Dockerfile for a FastAPI application?
Short Answer:
A production Dockerfile should be lightweight, secure, and leverage Docker’s layer caching. Architecturally, you should use a slim Python base image (e.g., python:3.11-slim), copy the requirements.txt before the rest of the application code to cache dependencies, avoid running the app as the root user for security, and expose the port to run the server using Uvicorn (or Gunicorn with Uvicorn workers).
Detailed Answer:
A poor Dockerfile will result in a 2GB image that takes 10 minutes to build. An optimized one takes 150MB and builds in seconds.
- Dependency Caching: Docker builds images layer by layer. If you copy
main.pybefore installingrequirements.txt, every time you change a small typo in your Python code, Docker will re-download and re-install every single package. By copying the requirements first, Docker caches that heavy layer unless the requirements file actually changes. - No Cache: Using
--no-cache-dirwith pip ensures you don't store raw downloaded package files inside the container, saving disk space.
Dockerfile
# English Code Example: Optimized FastAPI Dockerfile
# 1. Use a slim official image
FROM python:3.11-slim# 2. Set the working directory
WORKDIR /app# 3. Copy only requirements first to leverage Docker caching
COPY requirements.txt .# 4. Install dependencies without caching the raw downloads
RUN pip install --no-cache-dir -r requirements.txt# 5. Copy the rest of the application code
COPY . .# 6. Expose the port
EXPOSE 8000# 7. Start the server (Using Uvicorn here, but Gunicorn is better for multi-core VMs)
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
প্রশ্ন ৩৫: FastAPI অ্যাপ্লিকেশনের জন্য আপনি কীভাবে একটি অপ্টিমাইজড এবং প্রোডাকশন-রেডি Dockerfile লিখবেন?
সংক্ষিপ্ত উত্তর:
একটি প্রোডাকশন Dockerfile হালকা, নিরাপদ এবং Docker-এর লেয়ার ক্যাশিং ব্যবহার করার উপযোগী হওয়া উচিত। আর্কিটেকচারালি, আপনার একটি স্লিম (slim) Python বেস ইমেজ ব্যবহার করা উচিত, ডিপেন্ডেন্সি ক্যাশ করার জন্য অ্যাপের কোড কপি করার আগেই requirements.txt কপি করা উচিত, সিকিউরিটির জন্য রুট (root) ইউজার এড়িয়ে চলা উচিত এবং Uvicorn বা Gunicorn ব্যবহার করে সার্ভার চালু করা উচিত।
বিস্তারিত উত্তর:
খারাপভাবে লেখা Dockerfile ২ জিবির বিশাল ইমেজ তৈরি করবে যা বিল্ড হতে ১০ মিনিট সময় নেবে। অপ্টিমাইজড ইমেজ হবে ১৫০ এমবির এবং কয়েক সেকেন্ডে বিল্ড হবে।
১. ডিপেন্ডেন্সি ক্যাশিং: Docker লেয়ার বাই লেয়ার ইমেজ বিল্ড করে। আপনি যদি requirements.txt এর আগে main.py কপি করেন, তবে কোডের ছোট একটি বানান ঠিক করলেও Docker নতুন করে আবার সব প্যাকেজ ডাউনলোড করবে। কিন্তু আগে requirements ফাইল দিলে, ওই ফাইলে পরিবর্তন না আসা পর্যন্ত Docker প্যাকেজ ইনস্টলের ভারী লেয়ারটি ক্যাশ করে রাখবে।
২. নো ক্যাশ: pip এর সাথে --no-cache-dir ব্যবহার করলে ডাউনলোড করা র (raw) প্যাকেজগুলো কনটেইনারে জমা হয়ে থাকে না, ফলে স্পেস বাঁচে।
Dockerfile
# বাংলা কোড এক্সাম্পল: অপ্টিমাইজড FastAPI Dockerfile
# ১. অফিশিয়াল স্লিম ইমেজ ব্যবহার
FROM python:3.11-slim# ২. ওয়ার্কিং ডিরেক্টরি সেট করা
WORKDIR /app# ৩. Docker ক্যাশ ব্যবহারের জন্য আগে শুধু requirements কপি করা
COPY requirements.txt .# ৪. অপ্রয়োজনীয় ক্যাশ ফাইল না রেখেই প্যাকেজ ইনস্টল করা
RUN pip install --no-cache-dir -r requirements.txt# ৫. এবার অ্যাপ্লিকেশনের বাকি কোড কপি করা
COPY . .# ৬. পোর্ট এক্সপোজ করা
EXPOSE 8000# ৭. সার্ভার স্টার্ট করা
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Question 36: What is a Reverse Proxy (like NGINX), and why should it sit in front of your FastAPI server in production?
Short Answer:
A Reverse Proxy (like NGINX, Traefik, or Caddy) is an intermediary server that sits between the public internet and your backend FastAPI server. Architecturally, it is mandatory in production because Uvicorn/Gunicorn are designed strictly to run Python code efficiently; they are terrible at handling secure SSL/TLS termination (HTTPS), serving static files (images/CSS), mitigating DDoS attacks, and load balancing. The Reverse Proxy handles all these network-heavy tasks, forwarding only clean, legitimate HTTP requests to FastAPI.
Detailed Answer:
Never expose Uvicorn directly to port 80/443 on the open internet.
- SSL Termination: Encrypting and decrypting HTTPS traffic requires heavy CPU math. NGINX does this in highly optimized C code. It decrypts the request and forwards it to FastAPI over plain HTTP internally, keeping Python completely free for business logic.
- Static Files: If a user requests a profile picture, FastAPI would have to read it from disk and stream it, wasting Python threads. NGINX intercepts
/images/*routes and serves the files directly from the hard drive 10x faster. - Security & Rate Limiting: NGINX can block malicious IPs, hide your internal server topology, and buffer slow-client requests (Slowloris attacks) before they can exhaust Uvicorn’s worker threads.
প্রশ্ন ৩৬: Reverse Proxy (যেমন NGINX) কী, এবং প্রোডাকশনে এটি কেন আপনার FastAPI সার্ভারের সামনে রাখা উচিত?
সংক্ষিপ্ত উত্তর:
Reverse Proxy (যেমন NGINX, Traefik) হলো এমন একটি মধ্যস্থতাকারী সার্ভার যা পাবলিক ইন্টারনেট এবং আপনার FastAPI সার্ভারের মাঝখানে বসে থাকে। প্রোডাকশনে এটি বাধ্যতামূলক কারণ Uvicorn/Gunicorn শুধু Python কোড খুব ভালোভাবে রান করতে পারে; কিন্তু এরা SSL/TLS (HTTPS) হ্যান্ডল করতে, স্ট্যাটিক ফাইল (ছবি/CSS) সার্ভ করতে, DDoS অ্যাটাক ঠেকাতে এবং লোড ব্যালেন্সিং করতে খুবই দুর্বল। Reverse Proxy নেটওয়ার্কের এই ভারী কাজগুলো সামলে নিয়ে শুধু পরিষ্কার এবং সঠিক রিকোয়েস্টগুলোই FastAPI-এর কাছে পাঠায়।
বিস্তারিত উত্তর:
খোলা ইন্টারনেটে পোর্ট 80 বা 443 এ কখনোই সরাসরি Uvicorn উন্মুক্ত করা উচিত নয়।
১. SSL টার্মিনেশন: HTTPS এনক্রিপ্ট বা ডিক্রিপ্ট করতে প্রচুর প্রসেসর শক্তি লাগে। NGINX তার সি © কোড দিয়ে এটি অত্যন্ত ফাস্ট করে দেয়। সে রিকোয়েস্টটি ডিক্রিপ্ট করে সার্ভারের ভেতরে সাধারণ HTTP এর মাধ্যমে FastAPI-কে পাঠায়। এর ফলে Python-এর পুরো শক্তি শুধু বিজনেস লজিকে ব্যবহার করা যায়।
২. স্ট্যাটিক ফাইল: কেউ প্রোফাইল পিকচার দেখতে চাইলে FastAPI-কে ডিস্ক থেকে পড়ে সেটি পাঠাতে হতো, যা থ্রেড নষ্ট করত। NGINX নিজে থেকেই /images/* রিকোয়েস্টগুলো ধরে সরাসরি হার্ডডিস্ক থেকে ১০ গুণ বেশি স্পিডে পাঠিয়ে দেয়।
৩. সিকিউরিটি: NGINX ক্ষতিকর আইপি ব্লক করতে পারে এবং ধীরগতির রিকোয়েস্টগুলো (Slowloris অ্যাটাক) নিজে বাফার করে রাখে, যাতে এগুলো Uvicorn-এর ওয়ার্কারগুলোকে জ্যাম করতে না পারে।
Question 37: How do you implement robust API Versioning in FastAPI?
Short Answer:
API Versioning ensures that when you update your backend response structures, older mobile or web apps that haven’t been updated don’t suddenly crash. In FastAPI, the most standard architectural approach is URL Path Versioning. We implement this by mounting different APIRouter instances to distinct prefixes like /api/v1 and /api/v2 in the main application, effectively creating backward-compatible endpoints.
Detailed Answer:
As a 6-month developer, preparing for future breaking changes is a highly appreciated skill.
- Why Version? Imagine you change
user.nametouser.first_nameanduser.last_name. The old iOS app expectsnameand will crash if it receives the new format. - Implementation: You create two folders:
api/v1/andapi/v2/. You define the old logic inv1and the new logic inv2. Inmain.py, you mount both routers. The old iOS app continues hitting/api/v1/users, while your new React web app hits/api/v2/users.
Python
# English Code Example: API Versioning
from fastapi import FastAPI, APIRouterapp = FastAPI()# 1. Define V1 Router (Legacy)
router_v1 = APIRouter(prefix="/api/v1")
@router_v1.get("/users")
async def get_users_v1():
return [{"name": "John Doe"}] # Old format# 2. Define V2 Router (New)
router_v2 = APIRouter(prefix="/api/v2")
@router_v2.get("/users")
async def get_users_v2():
return [{"first_name": "John", "last_name": "Doe"}] # New format# 3. Mount both to the main app
app.include_router(router_v1)
app.include_router(router_v2)
প্রশ্ন ৩৭: FastAPI-তে আপনি কীভাবে শক্তিশালী API Versioning ইমপ্লিমেন্ট করবেন?
সংক্ষিপ্ত উত্তর:
API Versioning নিশ্চিত করে যে আপনি ব্যাকএন্ড আপডেট করলেও পুরনো মোবাইল বা ওয়েব অ্যাপগুলো (যারা আপডেট হয়নি) যেন হুট করে ক্র্যাশ না করে। FastAPI-তে এর সবচেয়ে স্ট্যান্ডার্ড পদ্ধতি হলো URL Path Versioning। এটি করার জন্য আমরা মূল অ্যাপ্লিকেশনে আলাদা আলাদা APIRouter কে /api/v1 এবং /api/v2 এর মতো আলাদা প্রিফিক্সে (prefix) যুক্ত করে দিই। এতে পুরনো ও নতুন দুই ধরনের ক্লায়েন্টই কোনো সমস্যা ছাড়া কাজ করতে পারে।
বিস্তারিত উত্তর:
৬ মাসের অভিজ্ঞতাসম্পন্ন ডেভেলপার হিসেবে ভবিষ্যতের পরিবর্তনের জন্য আগে থেকে প্রস্তুতি নেওয়াটা খুব দারুণ একটি স্কিল।
- ভার্সনিং কেন লাগে? ধরুন আপনি
user.nameকে পরিবর্তন করেuser.first_nameএবংuser.last_nameকরলেন। পুরনো iOS অ্যাপ এখনওnameখুঁজবে এবং নতুন ডেটা পেলে ক্র্যাশ করবে। - কীভাবে করবেন: আপনি
api/v1/এবংapi/v2/নামে দুটি ফোল্ডার বানাবেন।v1এ পুরনো লজিক এবংv2তে নতুন লজিক রাখবেন।main.pyতে দুটি রাউটারই যুক্ত করে দেবেন। পুরনো অ্যাপ/api/v1/usersথেকে ডেটা নেবে, আর নতুন ওয়েব অ্যাপ/api/v2/usersথেকে ডেটা নেবে।
Python
# বাংলা কোড এক্সাম্পল: API Versioning
from fastapi import FastAPI, APIRouterapp = FastAPI()# ১. V1 রাউটার (পুরনো)
router_v1 = APIRouter(prefix="/api/v1")
@router_v1.get("/users")
async def get_users_v1():
return [{"name": "John Doe"}] # পুরনো ফরম্যাট# ২. V2 রাউটার (নতুন)
router_v2 = APIRouter(prefix="/api/v2")
@router_v2.get("/users")
async def get_users_v2():
return [{"first_name": "John", "last_name": "Doe"}] # নতুন ফরম্যাট# ৩. দুটি রাউটারকেই মূল অ্যাপে যুক্ত করা
app.include_router(router_v1)
app.include_router(router_v2)
Question 38: How do you implement Pagination in FastAPI using Query Parameters?
Short Answer:
Pagination prevents your database from overloading by returning a massive list of records all at once. In FastAPI, pagination is natively implemented using Query Parameters with skip (offset) and limit (count) variables. By defining these parameters in the route function and giving them default values, FastAPI automatically parses them from the URL, validates their integers, and passes them to your SQLAlchemy database query.
Detailed Answer:
- The Problem: If you run
db.query(User).all()and you have 1 million users, the database attempts to load 1 million rows into Node/Python RAM, crashing the server instantly. - The Limit/Offset Approach: You tell the database: “Skip the first 20 results (
offset), and only give me the next 10 results (limit)." The client handles the math by modifying the URL:?skip=20&limit=10. - FastAPI Implementation: By setting
skip: int = 0, limit: int = 10in the route parameters, FastAPI handles all the type conversions. If a user maliciously tries?limit=abc, FastAPI intercepts it and returns a 422 Error automatically.
Python
# English Code Example: Pagination
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Sessionapp = FastAPI()@app.get("/items/")
# Default: Skip 0 items, fetch 10 items.
def read_items(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
# The database only processes the exact slice requested
items = db.query(Item).offset(skip).limit(limit).all()
return {
"data": items,
"page_info": {"skip": skip, "limit": limit}
}
প্রশ্ন ৩৮: Query Parameters ব্যবহার করে FastAPI-তে আপনি কীভাবে Pagination (পেজিনেশন) ইমপ্লিমেন্ট করবেন?
সংক্ষিপ্ত উত্তর:
পেজিনেশন ডেটাবেসকে একসাথে বিশাল ডেটা রিটার্ন করার ওভারলোড থেকে বাঁচায়। FastAPI-তে পেজিনেশন করার সবচেয়ে স্ট্যান্ডার্ড উপায় হলো Query Parameters-এর মাধ্যমে skip (অফসেট) এবং limit (পরিমাণ) ভেরিয়েবল ব্যবহার করা। রাউট ফাংশনে এই প্যারামিটারগুলোর ডিফল্ট ভ্যালু সেট করে দিলে, FastAPI নিজে থেকেই URL থেকে এগুলো পার্স করে, সংখ্যা কিনা তা ভ্যালিডেট করে এবং SQLAlchemy ডেটাবেস কুয়েরিতে পাঠিয়ে দেয়।
বিস্তারিত উত্তর:
- সমস্যা: আপনি যদি
db.query(User).all()রান করেন এবং আপনার ১০ লাখ ইউজার থাকে, তবে ডেটাবেস ওই ১০ লাখ রো সার্ভারের RAM-এ লোড করার চেষ্টা করবে এবং সার্ভার ক্র্যাশ করবে। - Limit/Offset মেথড: আপনি ডেটাবেসকে বলেন: “প্রথম ২০টি ডেটা বাদ দাও (
offset), এবং শুধু পরের ১০টি ডেটা দাও (limit)।" ক্লায়েন্ট URL পরিবর্তন করে এই হিসাব মেলায়:?skip=20&limit=10। - FastAPI ইমপ্লিমেন্টেশন: রাউটে
skip: int = 0, limit: int = 10লিখলে FastAPI নিজে থেকেই টাইপ কনভার্ট করে। ইউজার যদি ইচ্ছা করে?limit=abcপাঠায়, তবে FastAPI তা ধরে নিজে থেকেই 422 Error দিয়ে দেবে।
Python
# বাংলা কোড এক্সাম্পল: পেজিনেশন
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Sessionapp = FastAPI()@app.get("/items/")
# ডিফল্ট: 0 আইটেম স্কিপ করবে, 10 আইটেম ফেচ করবে।
def read_items(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
# ডেটাবেস শুধুমাত্র রিকোয়েস্ট করা নির্দিষ্ট পরিমাণ ডেটাই প্রসেস করবে
items = db.query(Item).offset(skip).limit(limit).all()
return {
"data": items,
"page_info": {"skip": skip, "limit": limit}
}
Question 39: How do you broadcast a WebSocket message to multiple connected clients simultaneously in FastAPI?
Short Answer:
To broadcast messages (e.g., in a group chat), you must architect a Connection Manager class. When clients connect, the manager stores their active WebSocket objects in a Python List or Set. When a message needs to be broadcasted, the manager iterates through the list of saved connections and calls await websocket.send_text(message) on every active socket in the array.
Detailed Answer:
Standard WebSockets only talk 1-to-1 (Client <-> Server). Broadcasting requires keeping state in memory.
- The Manager State: We create a class with an
active_connectionslist. - Connect/Disconnect: When a user hits the endpoint, we
accept()the socket and append it to the list. When they leave, we catch theWebSocketDisconnectexception and remove them from the list. - Broadcasting: A specific method in the manager loops over
active_connectionsand sends the payload. If you use Gunicorn with multiple workers in production, storing this list in Python memory won't work across workers; you must use a central pub/sub broker like Redis to broadcast events across all Python processes.
Python
# English Code Example: WebSocket Broadcasting
from fastapi import FastAPI, WebSocket, WebSocketDisconnectapp = FastAPI()# 1. The Connection Manager
class ConnectionManager:
def __init__(self):
self.active_connections: list[WebSocket] = [] async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket) async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)manager = ConnectionManager()# 2. The Route
@app.websocket("/ws/chat")
async def chat_endpoint(websocket: WebSocket):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
# Send message to EVERYONE connected
await manager.broadcast(f"User says: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast("A user left the chat")
প্রশ্ন ৩৯: FastAPI-তে একাধিক কানেক্টেড ক্লায়েন্টকে একসাথে WebSocket মেসেজ কীভাবে ব্রডকাস্ট করবেন?
সংক্ষিপ্ত উত্তর:
একাধিক জনকে একসাথে মেসেজ পাঠানোর জন্য (যেমন গ্রুপ চ্যাট) আপনাকে একটি Connection Manager ক্লাস আর্কিটেক্ট করতে হবে। ক্লায়েন্টরা কানেক্ট করার সাথে সাথে ম্যানেজার তাদের অ্যাকটিভ WebSocket অবজেক্টগুলোকে একটি Python List বা Set-এ সেভ করে রাখবে। যখন কোনো মেসেজ ব্রডকাস্ট করতে হবে, তখন ম্যানেজার ওই লিস্টে লুপ চালিয়ে সবার অ্যাকটিভ সকেটে await websocket.send_text(message) কল করে মেসেজটি পাঠিয়ে দেবে।
বিস্তারিত উত্তর:
সাধারণ ওয়েবসকেট শুধু 1-to-1 (ক্লায়েন্ট <-> সার্ভার) যোগাযোগ করে। ব্রডকাস্ট করার জন্য মেমরিতে স্টেট ধরে রাখতে হয়।
১. ম্যানেজার স্টেট: আমরা active_connections লিস্টসহ একটি ক্লাস বানাই।
২. Connect/Disconnect: ইউজার কানেক্ট করলে আমরা সকেটটি accept() করে লিস্টে যোগ করি। ইউজার লিভ নিলে WebSocketDisconnect এক্সেপশন ধরে তাকে লিস্ট থেকে রিমুভ করি।
৩. ব্রডকাস্টিং: ম্যানেজারের একটি নির্দিষ্ট মেথড সবার কানেকশনে লুপ চালিয়ে মেসেজ পাঠায়। তবে প্রোডাকশনে Gunicorn দিয়ে একাধিক ওয়ার্কার চালালে Python মেমরির এই লিস্ট কাজ করবে না। তখন সব প্রসেসে মেসেজ পাঠানোর জন্য Redis-এর মতো সেন্ট্রাল পাব/সাব ব্রোকার ব্যবহার করতে হবে।
Python
# বাংলা কোড এক্সাম্পল: WebSocket ব্রডকাস্টিং
from fastapi import FastAPI, WebSocket, WebSocketDisconnectapp = FastAPI()# ১. Connection Manager ক্লাস
class ConnectionManager:
def __init__(self):
self.active_connections: list[WebSocket] = [] async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket) async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)manager = ConnectionManager()# ২. ওয়েবসকেট রাউট
@app.websocket("/ws/chat")
async def chat_endpoint(websocket: WebSocket):
await manager.connect(websocket)
try:
while True:
data = await websocket.receive_text()
# কানেক্টেড সবাইকেই মেসেজ পাঠানো
await manager.broadcast(f"ইউজারের মেসেজ: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast("একজন ইউজার চ্যাট থেকে বিদায় নিয়েছেন")
Question 40: How do you implement Rate Limiting in FastAPI to protect your server from abuse?
Short Answer:
FastAPI does not have native, built-in rate limiting. To protect the server from spam or brute-force attacks, developers typically use third-party libraries like slowapi (which is based on the popular limits library). Architecturally, slowapi intercepts incoming requests, tracks the user's IP address (or user ID) using in-memory storage or Redis, and rejects excess requests by throwing an HTTP 429 Too Many Requests exception if the rate limit is exceeded.
Detailed Answer:
Rate limiting is mandatory for public APIs to prevent DDoS attacks and server crashes.
- The Setup: You initialize a
Limiterobject, usually setting a default key function likeget_remote_address(to track IPs). - The Decorator: You attach the limiter to specific routes using
@limiter.limit("5/minute"). - The Request Object: The route function must accept the FastAPI
Requestobject as a parameter becauseslowapineeds it to extract the IP address or headers to track the usage count accurately.
Python
# English Code Example: Rate Limiting with SlowAPI
# pip install slowapi
from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded# 1. Initialize the Limiter using the client's IP address
limiter = Limiter(key_func=get_remote_address)app = FastAPI()# 2. Attach the limiter to the FastAPI app and register the custom 429 error handler
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)# 3. Apply the rate limit to a specific route
@app.get("/login")
@limiter.limit("5/minute") # Strict limit: 5 requests per minute per IP
async def login(request: Request):
# 'request' parameter is mandatory for the limiter to inspect the IP
return {"message": "Login attempt successful"}
প্রশ্ন ৪০: সার্ভারকে স্প্যাম থেকে বাঁচাতে FastAPI-তে আপনি কীভাবে Rate Limiting ইমপ্লিমেন্ট করবেন?
সংক্ষিপ্ত উত্তর:
FastAPI-এর নিজস্ব কোনো বিল্ট-ইন রেট লিমিটিং নেই। সার্ভারকে স্প্যাম বা ব্রুট-ফোর্স অ্যাটাক থেকে বাঁচাতে ডেভেলপাররা সাধারণত slowapi এর মতো থার্ড-পার্টি লাইব্রেরি ব্যবহার করেন। আর্কিটেকচারালি, slowapi রিকোয়েস্টগুলোকে আটকে দেয়, মেমরি বা Redis ব্যবহার করে ইউজারের IP অ্যাড্রেস (বা আইডি) ট্র্যাক করে, এবং লিমিট পার হয়ে গেলে নতুন রিকোয়েস্টগুলোকে HTTP 429 Too Many Requests এক্সেপশন দিয়ে রিজেক্ট করে দেয়।
বিস্তারিত উত্তর:
DDoS অ্যাটাক এবং সার্ভার ক্র্যাশ ঠেকাতে পাবলিক API-তে রেট লিমিটিং থাকা বাধ্যতামূলক।
- সেটআপ: আপনাকে একটি
Limiterঅবজেক্ট ইনিশিয়ালাইজ করতে হবে এবং ডিফল্ট কি (key) হিসেবেget_remote_addressসেট করতে হবে, যা IP ট্র্যাক করবে। - ডেকোরেটর: নির্দিষ্ট রাউটের ওপরে
@limiter.limit("5/minute")ডেকোরেটর বসিয়ে লিমিট সেট করতে হয়। - রিকোয়েস্ট অবজেক্ট: রাউট ফাংশনে অবশ্যই FastAPI-এর
Requestঅবজেক্টটি প্যারামিটার হিসেবে রিসিভ করতে হবে। কারণslowapiএই রিকোয়েস্ট অবজেক্ট থেকেই IP বা হেডার বের করে ব্যবহারের হিসাব রাখে।
Python
# বাংলা কোড এক্সাম্পল: SlowAPI দিয়ে রেট লিমিটিং
# pip install slowapi
from fastapi import FastAPI, Request
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded# ১. ক্লায়েন্টের IP অ্যাড্রেস ব্যবহার করে Limiter ইনিশিয়ালাইজ করা
limiter = Limiter(key_func=get_remote_address)app = FastAPI()# ২. FastAPI অ্যাপের সাথে যুক্ত করা এবং 429 এরর হ্যান্ডলার রেজিস্টার করা
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)# ৩. নির্দিষ্ট রাউটে লিমিট অ্যাপ্লাই করা
@app.get("/login")
@limiter.limit("5/minute") # কড়া নিয়ম: প্রতি মিনিটে একটি IP থেকে সর্বোচ্চ ৫টি রিকোয়েস্ট
async def login(request: Request):
# আইপি স্ক্যান করার জন্য 'request' প্যারামিটারটি থাকা বাধ্যতামূলক
return {"message": "লগইন চেষ্টা সফল হয়েছে"}
(আপনার Python এবং FastAPI সিরিজের ৪০টি প্রশ্ন এখানেই সফলভাবে শেষ হলো। ৬ মাসের অভিজ্ঞতাকে সামনে রেখে প্রতিটি প্রশ্নের উত্তর এমনভাবে সাজানো হয়েছে, যাতে আপনি কনফিডেন্সের সাথে যেকোনো ইন্টারভিউ ফেস করতে পারেন!)



