Project 2: Notes API + Flutter App
নিজের backend, নিজের Flutter app
এই project এ তুমি প্রথমবার নিজের পুরো product বানাবে: নিজের backend, নিজের Flutter app, দুটো একসাথে কথা বলবে। এখন থেকে AI ব্যবহার করতে পারো, কিন্তু নিয়ম একটাই: AI এর লেখা প্রতিটা লাইন তুমি পড়বে আর বুঝবে, আর test তোমার ঠিক করা ক্ষেত্রগুলো যাচাই করবে। শেষে এটা তোমার portfolio এর প্রথম বড় project হবে।
#কী বানাবো: "নোটখাতা"
একটা note নেওয়ার app, যেটা ফোন আর web দুই জায়গাতেই চলে:
- Email/password দিয়ে register আর login
- Note তৈরি, পড়া, বদলানো, মুছে ফেলা
- Pin করা note সবার উপরে
- Note এ একটা ছবি যোগ করা
- Title আর লেখায় খোঁজা
- Tag দিয়ে ভাগ করা
- Infinite scroll (cursor pagination)
- Logout, আর সব device থেকে logout
#Non-functional requirement
| বিষয় | লক্ষ্য |
|---|---|
| Security | অন্যের note কোনোভাবেই দেখা যাবে না (test দিয়ে প্রমাণিত) |
| Performance | List API ৫০০০ note থাকলেও ১০০ ms এর নিচে (local এ) |
| দুর্বল network | Retry এ duplicate note হবে না (idempotency key) |
| Token | Access ১৫ মিনিট, refresh ৩০ দিন, rotation সহ |
| Test | Backend এ coverage ৮০%+, সব permission এর test |
| Code quality | Ruff, Pyright পরিষ্কার |
#Architecture
#API এর তালিকা
| Method | Path | কাজ |
|---|---|---|
| POST | /v1/auth/register |
নতুন account |
| POST | /v1/auth/login |
Token জোড়া |
| POST | /v1/auth/refresh |
নতুন token জোড়া (rotation) |
| POST | /v1/auth/logout |
এই device এর refresh token বাতিল |
| POST | /v1/auth/logout-all |
সব device |
| GET | /v1/me |
নিজের profile |
| GET | /v1/notes?cursor=&limit=&search=&tag= |
Note এর list |
| POST | /v1/notes |
নতুন (Idempotency-Key header সহ) |
| GET | /v1/notes/{id} |
একটা note |
| PATCH | /v1/notes/{id} |
বদলানো |
| DELETE | /v1/notes/{id} |
মুছে ফেলা (soft) |
| POST | /v1/notes/{id}/image |
ছবি upload |
| GET | /v1/tags |
নিজের সব tag |
#Database
users (id, email UNIQUE, name, password_hash, is_active, created_at, updated_at)
refresh_tokens (id, user_id FK, token_hash UNIQUE, device_name, expires_at, revoked_at, created_at)
notes (id, owner_id FK, title, content, is_pinned, image_key, idempotency_key,
deleted_at, created_at, updated_at)
UNIQUE (owner_id, idempotency_key)
INDEX (owner_id, is_pinned, updated_at DESC)
tags (id, owner_id FK, name) UNIQUE (owner_id, name)
note_tags (note_id FK, tag_id FK) PRIMARY KEY (note_id, tag_id)#কাজের ধাপ (প্রায় ৪ সপ্তাহ)
সপ্তাহ ১: Backend এর ভিত্তি
- বড় Project এর Structure অনুযায়ী folder
- Config, database, model, Alembic migration
- Register, login,
/me, আর তাদের test
সপ্তাহ ২: Notes
- Note এর CRUD, মালিকানা যাচাই
- Cursor pagination, search, tag
- Idempotency key
- IDOR test সহ সব test
সপ্তাহ ৩: Flutter app
- Login/register screen, token secure storage এ
- Dio interceptor: token যোগ, 401 এ refresh, refresh ব্যর্থ হলে login এ ফেরা (Flutter সংযোগ অধ্যায় আগে একবার দেখে নিতে পারো)
- Note list (infinite scroll, pull to refresh), detail, edit, search
সপ্তাহ ৪: ছবি, polish, README
- ছবি upload আর দেখানো
- Error গুলো সুন্দর করে দেখানো (
error.codeঅনুযায়ী) - Empty state, loading state, offline এ বার্তা
- README আর demo video
#Flutter দিকের কিছু খুঁটিনাটি
// Idempotency: button চাপার সময় key বানাও, retry তে একই key
final key = const Uuid().v4();
await dio.post('/v1/notes',
data: note.toJson(),
options: Options(headers: {'Idempotency-Key': key}));// Error parse: সব endpoint এর error একই ধরনে আসে
class ApiError {
final String code;
final String message;
final Map<String, String> fields;
// ...
}Android emulator থেকে Mac এর localhost: http://10.0.2.2:8000। আসল phone থেকে: Mac এর local IP (192.168.x.x:8000) আর uvicorn কে --host 0.0.0.0 দিয়ে চালাতে হবে।
#Review checklist (AI এর code সহ)
#শেষ হলে
- GitHub এ দুইটা repo (backend, flutter) বা একটা monorepo
- README তে: কী, কেন, architecture এর ছবি, কীভাবে চালাবে, API এর তালিকা, screenshot, কী শিখলে
- ২ মিনিটের demo video: login, note তৈরি, ছবি, search, অন্য user দিয়ে প্রমাণ যে অন্যের note দেখা যায় না
- Month 5 এ এটা Docker এ ভরে internet এ deploy করবে
#নিজেকে যাচাই করো
ছবি দেখার সময় কেন API দিয়ে না এনে সরাসরি storage/CDN এর URL ব্যবহার করা ভালো?
ছবি বড় আর বারবার দেখা হয়। API দিয়ে আনলে প্রতিবার app server এর worker আর bandwidth খরচ হয়। Storage বা CDN থেকে সরাসরি আনলে দ্রুত, সস্তা, আর app server এর চাপ কমে। ব্যক্তিগত ছবি হলে মেয়াদি signed URL ব্যবহার করা যায়।
Note এর table এ `(owner_id, idempotency_key)` UNIQUE কেন, শুধু `idempotency_key` না কেন?
Key টা client বানায়, তাই দুইজন আলাদা user এর key দৈবাৎ মিলতে পারে (খুব কম সম্ভাবনা হলেও)। User ভিত্তিক unique রাখলে একজনের key আরেকজনের কাজে বাধা দেয় না, আর একজন user অন্যের key আন্দাজ করে তার note পাওয়ার সুযোগও থাকে না।
চিহ্ন দিলে sidebar আর রোডম্যাপে এই অধ্যায়ের station ভরে যাবে।