Data Layer & API
The Blogify data layer is built on a hybrid architecture that leverages Supabase as a Backend-as-a-Service (BaaS) and FastAPI for high-performance logic. The frontend interacts with these services through a centralized TypeScript library and Next.js Route Handlers to ensure type safety and secure authentication.
Data Architecture
Blogify utilizes Supabase (PostgreSQL) for its relational data, Supabase Auth for identity management, and Supabase Storage for handling media uploads. Communication is abstracted through src/lib/api.ts, which provides a clean, functional interface for the UI components.
Core Data Models
The following TypeScript interfaces define the primary entities within the system:
export interface Post {
id: string;
title: string;
content: string; // Markdown supported
user_id: string;
tags: string[];
image_url?: string;
created_at: string;
updated_at: string | null;
}
export interface User {
id: string;
name: string;
email: string;
profile_picture_url?: string;
created_at: string;
}
Authentication Layer
Authentication is handled via a combination of Supabase client-side sessions and Next.js server-side route handlers to prevent the exposure of sensitive keys.
Login & Signup
The application uses dedicated API routes (/api/auth/login and /api/auth/signup) to proxy requests to Supabase. This allows for additional server-side logic, such as normalizing error messages or creating default profiles.
Usage Example:
import { signup } from '@/lib/api';
const newUser = await signup({
name: "Jane Doe",
email: "jane@example.com",
password: "securePassword123"
});
Post Management (CRUD)
The lib/api.ts module provides asynchronous functions to manage blog content. Most write operations require an active user session verified via Row Level Security (RLS).
Fetching Posts
getPosts supports pagination and filtering by user ID.
// Fetch the latest 6 posts for the feed
const posts = await getPosts(0, 6);
// Fetch posts by a specific author
const userPosts = await getPosts(0, 20, "user_uuid_here");
Creating & Updating
Write operations accept specific DTOs (PostCreate or PostUpdate) to ensure only mutable fields are sent to the database.
const newPost = await createPost({
title: "My First Blog",
content: "# Hello World",
user_id: "current_user_id",
tags: ["intro", "tech"]
});
Media & Storage
Blogify handles image uploads (featured images and images within Markdown) through Supabase Storage buckets.
uploadImage(file: File)
This function handles the file naming convention (using timestamps and random hashes) and returns a public URL for use in the database.
const file = e.target.files[0];
const publicUrl = await uploadImage(file);
// publicUrl can now be saved to a Post or User profile
Error Handling & Mapping
To keep the UI code clean, the data layer includes internal mapping functions. These functions:
- Normalize Keys: Convert database-style keys (e.g.,
profile_picture) to frontend camelCase or preferred naming (profile_picture_url). - Safety Defaults: Ensure arrays (like
tags) are initialized as empty lists rather thannull. - Error Bubbling: Throws descriptive errors that can be caught by UI "Error Boundary" components or Toast notifications.
API Response Handling
All API calls are wrapped in try-catch blocks within the components. In the event of a failure (e.g., network error or unauthorized access), the Feed and Post components are designed to show specific error states and retry buttons.