Part 9: Building New Features — What It's Like to Be a Developer
A practical guide for developers on how to add new features to AllBengal while keeping code organized and safe.
The Goal: Make It Easy to Add Stuff
When I was building AllBengal, I thought: "Other developers might want to add features. How do I make that easy?"
The answer is: organize everything so it's obvious where new code goes, and use the same patterns everywhere.
Think of it like building with LEGO blocks:
- Every block has the same connector style
- You don't have to think about how to snap them together
- Just follow the pattern and add your piece
The AllBengal Code Organization
backend/ ├── app/ │ ├── core/ ← Shared infrastructure │ │ ├── database.py ← How to talk to database │ │ ├── security.py ← Login & tokens │ │ ├── config.py ← Settings │ │ └── permissions.py ← Who can do what │ │ │ └── features/ ← Features (auth, blog, media, etc.) │ ├── auth/ │ │ ├── router.py ← What HTTP endpoints exist │ │ ├── schemas.py ← Data validation │ │ └── crud.py ← Database queries │ ├── blog/ │ │ ├── router.py │ │ ├── schemas.py │ │ └── crud.py │ └── [other features] │ └── main.py ← Start here frontend/ ├── src/ │ ├── lib/ │ │ ├── components/ ← Reusable UI pieces │ │ ├── server/ ← Backend communication │ │ └── utils/ ← Helper functions │ │ │ └── routes/ ← Website pages │ ├── +page.svelte ← Home page │ ├── blog/ │ │ ├── +page.server.ts ← Load data from backend │ │ └── [slug]/ ← Individual blog post │ └── [other pages] │ └── package.json ← Dependencies
How to Add a New Feature
Step 1: Create the Backend Folder
If you want to add a "comments" feature:
backend/app/features/comments/ ├── router.py ← The HTTP endpoints ├── schemas.py ← What data looks like └── crud.py ← Database queries
Step 2: Write the Data Models (schemas.py)
This defines what your data looks like:
// app/features/comments/schemas.py
from pydantic import BaseModel
class CommentCreate(BaseModel):
post_id: int
content: str
class Comment(BaseModel):
id: int
post_id: int
author_id: int
content: str
created_at: str
This is like describing: "A comment has these fields with these types."
Step 3: Write Database Queries (crud.py)
CRUD = Create, Read, Update, Delete
// app/features/comments/crud.py
async def create_comment(post_id: int, author_id: int, content: str):
query = """
INSERT INTO comments (post_id, author_id, content)
VALUES ($1, $2, $3)
RETURNING id, post_id, author_id, content, created_at
"""
return await db.execute(query, (post_id, author_id, content))
async def get_post_comments(post_id: int):
query = """
SELECT id, post_id, author_id, content, created_at
FROM comments
WHERE post_id = $1
ORDER BY created_at DESC
"""
return await db.fetch_all(query, (post_id,))
Step 4: Write the API Endpoints (router.py)
These are the HTTP endpoints your frontend will call:
// app/features/comments/router.py
from fastapi import APIRouter, Depends, HTTPException
from . import crud, schemas
router = APIRouter(prefix="/comments", tags=["Comments"])
@router.post("/")
async def create_comment(
comment: schemas.CommentCreate,
token_data: dict = Depends(get_token_data)
):
author_id = token_data["user_id"]
post = await crud.get_post_by_id(comment.post_id)
if not post or post["status"] != "published":
raise HTTPException(status_code=404, detail="Post not found")
new_comment = await crud.create_comment(
post_id=comment.post_id,
author_id=author_id,
content=comment.content
)
return new_comment
@router.get("/{post_id}")
async def get_comments(post_id: int):
return await crud.get_post_comments(post_id)
Step 5: Register the Router
Add it to backend/main.py:
from app.features.comments.router import router as comments_router app.include_router(comments_router, prefix=settings.API_PREFIX)
Now your endpoints are available at /api/comments/
Step 6: Create the Frontend Page
Make it easy for users to use your feature with beautiful, interactive UI.
Key Patterns to Follow
1. Always Validate Input
class CommentCreate(BaseModel):
content: str # Validates: must be string, must not be empty
Bad:
def create_comment(data): # What is data? Could be anything!
2. Always Check Permissions
is_author = comment["author_id"] == token_data["user_id"]
if not is_author:
raise HTTPException(status_code=403, detail="Not authorized")
Bad:
delete_comment(comment_id) # No permission check!
The LEGO Block Principle
Every feature should follow the same three-file pattern:
- schemas.py: Define data shapes with Pydantic
- crud.py: Write database queries
- router.py: Write HTTP endpoints that use crud and validate with schemas
This consistency means once you've added one feature, adding the next one is obvious. You just follow the same template.
Testing Your Feature
Before publishing, test:
- ✅ Can logged-in users create?
- ✅ Can non-logged-in users see "Permission Denied"?
- ✅ Are inputs validated?
- ✅ Do invalid inputs show friendly errors?
- ✅ Does the frontend work?
The Bottom Line
Adding features to AllBengal is easy because:
- ✅ Every feature has the same structure
- ✅ You know where code goes
- ✅ Tests are straightforward
- ✅ Other developers understand your code
- ✅ Like LEGO blocks: snap it in and it works
Want to build a feature? Drop a comment with your idea!
