Skip to content

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.

// 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.

// src/pages/PostDetail.tsx — updated excerpt
import { 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”
LayerWhat it does
FrontendHides the button — UX convenience, not security
BackendReturns 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.

if (!confirm('Delete this post?')) return

window.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.

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.

Open src/pages/PostDetail.tsx in your Bulletin project.

  1. Add deletePost to src/api/postsApi.ts.
  2. Import useAuth and useNavigate in PostDetail.tsx.
  3. Add handleDelete with a confirm check and navigation on success.
  4. Render the delete button conditionally: only when user?.userId === post.userId.
  5. 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() calls DELETE /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.