Deleting Your Own Post
Users should only be able to delete their own posts. The delete button appears only when the logged-in user’s ID matches the post’s userId — this is a frontend guard. The backend enforces the same rule: a DELETE /posts/:id request from the wrong user returns 403.
The deletePost API call
Section titled “The deletePost API call”// src/api/postsApi.ts (add to existing file)export async function deletePost(id: number): Promise<void> { await apiClient.delete(`/posts/${id}`)}No response body to parse — DELETE returns 204 No Content on success.
Adding delete to the PostDetail page
Section titled “Adding delete to the PostDetail page”// src/pages/PostDetail.tsx — updated excerptimport { useNavigate } from 'react-router-dom'import { useAuth } from '../context/AuthContext'import { getPost, deletePost, PostDetail as PostDetailType } from '../api/postsApi'
export default function PostDetail() { const { id } = useParams<{ id: string }>() const { user } = useAuth() const navigate = useNavigate() const [post, setPost] = useState<PostDetailType | null>(null) // ... loading/error state unchanged
async function handleDelete() { if (!post) return if (!confirm('Delete this post?')) return try { await deletePost(post.id) navigate('/') } catch { alert('Failed to delete post.') } }
// in JSX, after rendering post body: return ( <main> <h1>{post.title}</h1> <p>by {post.username} · {post.upvotes} votes</p> {user?.userId === post.userId && ( <button onClick={handleDelete}>Delete Post</button> )} <p>{post.body}</p> {/* ... comments section */} </main> )}user?.userId === post.userId compares the logged-in user’s ID against the post’s author ID. If they match, the delete button renders. If not — or if no one is logged in — it’s invisible.
Why you need both frontend and backend checks
Section titled “Why you need both frontend and backend checks”| Layer | What it does |
|---|---|
| Frontend | Hides the button — UX convenience, not security |
| Backend | Returns 403 if the user isn’t the author — the real enforcement |
A user who knows the API endpoint can call DELETE /posts/5 without a browser. The backend authenticateToken and ownership check in the Express controller are what actually prevent unauthorized deletes. The frontend check is a usability feature.
window.confirm — simple but functional
Section titled “window.confirm — simple but functional”if (!confirm('Delete this post?')) returnwindow.confirm blocks and returns true or false. It’s not pretty, but it prevents accidental deletes without adding a custom modal. For a real production app you’d build a styled confirmation dialog — for Bulletin it’s pragmatic.
Navigating away after delete
Section titled “Navigating away after delete”navigate('/')After deletion the post no longer exists, so staying on /posts/:id would show a 404 or error. Navigate to the home feed where the deleted post is now absent.
Exercise
Section titled “Exercise”Open src/pages/PostDetail.tsx in your Bulletin project.
- Add
deletePosttosrc/api/postsApi.ts. - Import
useAuthanduseNavigateinPostDetail.tsx. - Add
handleDeletewith aconfirmcheck and navigation on success. - Render the delete button conditionally: only when
user?.userId === post.userId. - Test it: log in as the post author and delete a post. Confirm you’re redirected to the home feed and the post is gone. Log in as a different user and confirm the button does not appear on someone else’s post.
deletePost()callsDELETE /posts/:id; no response body.- Show the delete button only when
user?.userId === post.userId. - The backend enforces ownership — the frontend check is UX only.
- Navigate to
/after deletion so the user doesn’t land on a dead URL.