Skip to content

Articles & Content

CRUD system for managing articles with publish/draft status.

Features

  • Create articles with title and content
  • Read articles (own + published)
  • Update articles (own only)
  • Delete articles (own only)
  • Publish/Draft status
  • Unlimited articles (all tiers)
  • Author attribution

Quick Start

Create Article (Frontend)

Visit: http://localhost:5173/dashboard/my-articles

Click "New Article" → Fill form → Save

Create Article (API)

Bash
1
2
3
4
5
6
7
8
curl -X POST http://localhost:8020/api/articles \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My First Article",
    "content": "Article content here",
    "is_published": true
  }'

Frontend Usage

List Articles

TypeScript
import { useArticles } from '@/hooks/api/useArticles';

const MyArticles = () => {
  const { data: articles, isLoading } = useArticles();

  return (
    <div>
      {articles?.map(article => (
        <ArticleCard key={article.id} article={article} />
      ))}
    </div>
  );
};

Create Article

TypeScript
import { useCreateArticle } from '@/hooks/api/useCreateArticle';

const CreateArticle = () => {
  const createMutation = useCreateArticle();

  const handleSubmit = (data: ArticleCreate) => {
    createMutation.mutate(data, {
      onSuccess: () => navigate('/dashboard/my-articles')
    });
  };

  return <ArticleForm onSubmit={handleSubmit} />;
};

Update Article

TypeScript
import { useUpdateArticle } from '@/hooks/api/useUpdateArticle';

const EditArticle = ({ id }) => {
  const updateMutation = useUpdateArticle(id);

  const handleSubmit = (data: ArticleUpdate) => {
    updateMutation.mutate(data);
  };

  return <ArticleForm onSubmit={handleSubmit} />;
};

Backend Implementation

Models

Python
# app/models.py
class Article(UUIDModelBase, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    title: str
    content: str
    author: str
    published_at: Optional[datetime.datetime]
    is_published: bool = Field(default=False)
    user_id: uuid.UUID = Field(foreign_key="user.id")
    user: "User" = Relationship(back_populates="articles")

Service

Python
# app/services/article_service.py
class ArticleService:
    async def create_article(self, article_data: ArticleCreate):
        article = Article(
            **article_data.model_dump(),
            user_id=self.current_user.user_id,
            author=self.current_user.email
        )
        self.db.add(article)
        await self.db.commit()
        return article

Ownership Check

Users can only edit/delete their own articles:

Python
1
2
3
# Automatic ownership validation
if article.user_id != self.current_user.user_id:
    raise HTTPException(403, "Can only edit own articles")

API Endpoints

  • GET /api/articles - List articles
  • POST /api/articles - Create article
  • GET /api/articles/{id} - Get single article
  • PUT /api/articles/{id} - Update article
  • DELETE /api/articles/{id} - Delete article

Full API reference

Database Schema

SQL
1
2
3
4
5
6
7
8
9
CREATE TABLE article (
    id SERIAL PRIMARY KEY,
    title VARCHAR NOT NULL,
    content TEXT NOT NULL,
    author VARCHAR NOT NULL,
    published_at TIMESTAMP,
    is_published BOOLEAN DEFAULT FALSE,
    user_id UUID REFERENCES user(id)
);

Common Tasks

Publish Article

Python
1
2
3
# Update is_published to True
article_data = ArticleUpdate(is_published=True)
await article_service.update_article(article_id, article_data)

Filter Published Only

Python
1
2
3
4
5
# Backend
articles = await article_service.get_all_articles(published_only=True)

# Frontend
const { data } = useArticles(true); // published only

Files Reference

  • app/models.py - Article model
  • app/services/article_service.py - Business logic
  • app/controllers/article.py - API endpoints
  • app/schemas/article.py - Request/response models
  • frontend/src/pages/Article/ - Article pages
  • frontend/src/hooks/api/useArticles.ts - React hooks