Base URL: http://localhost:5000/api
Most endpoints require authentication via Bearer token from Supabase Auth.
Include in request headers:
Authorization: Bearer <supabase_access_token>
Get all roadmaps with optional filtering and sorting.
Query Parameters:
resourceType(optional):free,paid, ormixedtargetRole(optional): Filter by target role (e.g., "Frontend Developer")sortBy(optional):trending(default) ornewest
Response:
[
{
"id": "uuid",
"title": "Frontend Developer Path 2025",
"target_role": "Beginner",
"creator_id": "uuid",
"upvotes": 15,
"downvotes": 2,
"created_at": "2025-10-05T10:00:00Z",
"updated_at": "2025-10-05T10:00:00Z",
"learning_steps": [
{
"id": "uuid",
"title": "Learn HTML & CSS",
"description": "Master the basics",
"order": 1,
"resources": [
{
"id": "uuid",
"title": "MDN Web Docs",
"url": "https://developer.mozilla.org",
"type": "article",
"cost": "FREE",
"description": "Complete guide"
}
]
}
]
}
]Get a single roadmap by ID.
Parameters:
id: Roadmap UUID
Response:
{
"id": "uuid",
"title": "Frontend Developer Path 2025",
"target_role": "Beginner",
"creator_id": "uuid",
"upvotes": 15,
"downvotes": 2,
"created_at": "2025-10-05T10:00:00Z",
"learning_steps": [...],
"roadmap_tags": [
{
"tags": {
"id": "uuid",
"name": "React",
"slug": "react"
}
}
],
"user_profiles": {
"full_name": "John Doe",
"name": "John",
"avatar_url": "https://...",
"email": "user@example.com"
}
}Error Responses:
404: Roadmap not found
Create a new roadmap (authentication required).
Request Body:
{
"title": "Frontend Developer Path 2025",
"target_role": "Beginner", // Optional, kept for backward compatibility
"learning_steps": [
{
"title": "Learn HTML & CSS",
"description": "Master the basics of web development",
"resources": [
{
"title": "MDN Web Docs",
"url": "https://developer.mozilla.org",
"type": "article",
"cost": "FREE",
"description": "Comprehensive guide"
}
]
}
]
}Note: After creating a roadmap, use the Tags API to add tags to it.
Response:
201: Created roadmap object400: Missing required fields401: Not authenticated500: Server error
Update a roadmap (creator only).
Parameters:
id: Roadmap UUID
Request Body:
{
"title": "Updated Title",
"target_role": "Updated Role"
}Response:
200: Updated roadmap object401: Not authenticated403: Not authorized (not creator)500: Server error
Delete a roadmap (creator only).
Parameters:
id: Roadmap UUID
Response:
200: { "message": "Roadmap deleted successfully" }401: Not authenticated403: Not authorized (not creator)500: Server error
Get the authenticated user's vote for a roadmap.
Parameters:
roadmapId: Roadmap UUID
Authentication: Required
Response:
{
"id": "uuid",
"roadmap_id": "uuid",
"user_id": "uuid",
"vote_type": "UPVOTE",
"created_at": "2025-10-05T10:00:00Z"
}Or null if user hasn't voted.
Cast or update a vote (authentication required).
Request Body:
{
"roadmap_id": "uuid",
"vote_type": "UPVOTE" // or "DOWNVOTE"
}Behavior:
- If no vote exists: Creates new vote
- If same vote type: Removes vote (toggle off)
- If different vote type: Updates to new vote type
Response:
{
"message": "Vote created",
"vote": {
"id": "uuid",
"roadmap_id": "uuid",
"user_id": "uuid",
"vote_type": "UPVOTE",
"created_at": "2025-10-05T10:00:00Z"
},
"roadmap_votes": {
"upvotes": 16,
"downvotes": 2
}
}Error Responses:
400: Invalid vote data401: Not authenticated500: Server error
Remove a vote (authentication required).
Parameters:
roadmapId: Roadmap UUID
Response:
{
"message": "Vote removed",
"roadmap_votes": {
"upvotes": 15,
"downvotes": 2
}
}Get all comments for a roadmap.
Parameters:
roadmapId: Roadmap UUID
Response:
[
{
"id": "uuid",
"roadmap_id": "uuid",
"user_id": "uuid",
"content": "Great roadmap! Very helpful.",
"created_at": "2025-10-05T10:00:00Z",
"updated_at": "2025-10-05T10:00:00Z",
"user_profiles": {
"full_name": "John Doe",
"name": "John",
"avatar_url": "https://...",
"email": "user@example.com"
}
}
]Create a comment (authentication required).
Request Body:
{
"roadmap_id": "uuid",
"content": "Great roadmap! Very helpful."
}Response:
201: Created comment object400: Comment content is required401: Not authenticated500: Server error
Update a comment (author only).
Parameters:
id: Comment UUID
Request Body:
{
"content": "Updated comment text"
}Response:
200: Updated comment object400: Comment content is required401: Not authenticated403: Not authorized (not author)500: Server error
Delete a comment (author only).
Parameters:
id: Comment UUID
Response:
200: { "message": "Comment deleted successfully" }401: Not authenticated403: Not authorized (not author)500: Server error
Get all tags with optional search.
Query Parameters:
limit(optional): Number of tags to return (default: 50)search(optional): Search tags by name
Response:
[
{
"id": "uuid",
"name": "React",
"slug": "react",
"usage_count": 25,
"created_at": "2025-10-05T10:00:00Z"
}
]Get popular tags (most used).
Query Parameters:
limit(optional): Number of tags to return (default: 20)
Response:
[
{
"id": "uuid",
"name": "React",
"slug": "react",
"usage_count": 25,
"created_at": "2025-10-05T10:00:00Z"
}
]Get or create a tag (authentication required).
Request Body:
{
"name": "React"
}Behavior:
- If tag exists (by slug): Returns existing tag
- If tag doesn't exist: Creates new tag with auto-generated slug
- Handles race conditions gracefully
Response:
{
"id": "uuid",
"name": "React",
"slug": "react",
"usage_count": 0,
"created_at": "2025-10-05T10:00:00Z"
}Error Responses:
400: Tag name is required401: Not authenticated500: Server error
Add tags to a roadmap (roadmap creator only).
Parameters:
id: Roadmap UUID
Request Body:
{
"tag_ids": ["uuid1", "uuid2", "uuid3"]
}Response:
200: { "message": "Tags added successfully" }400: tag_ids array is required401: Not authenticated403: Not authorized (not creator)500: Server error
Remove a tag from a roadmap (roadmap creator only).
Parameters:
id: Roadmap UUIDtagId: Tag UUID
Response:
200: { "message": "Tag removed successfully" }401: Not authenticated403: Not authorized (not creator)500: Server error
The TagInput component (frontend/src/components/TagInput.jsx) provides:
- Free-form text input (Enter, comma, or space to add tags)
- Popular tag suggestions
- Duplicate prevention
- Temporary tag objects with
isNew: trueflag
Flow:
- User types tags in TagInput component
- Tags are stored locally with temporary IDs and
isNew: trueflag - On form submission:
- Create roadmap
- For each tag with
isNew: true, callPOST /api/tagsto get/create tag - Collect all tag IDs
- Call
POST /api/roadmaps/:id/tagsto link tags
Example:
const handleSubmit = async (e) => {
const roadmap = await roadmapsAPI.create(roadmapData);
if (selectedTags.length > 0) {
const tagIds = await Promise.all(
selectedTags.map(async (tag) => {
if (tag.isNew) {
const createdTag = await tagsAPI.create(tag.name);
return createdTag.id;
}
return tag.id;
})
);
await tagsAPI.addToRoadmap(roadmap.id, tagIds);
}
};All errors return JSON in this format:
{
"error": "Error message description"
}200- Success201- Created400- Bad Request (invalid data)401- Unauthorized (not authenticated)403- Forbidden (not authorized)404- Not Found500- Internal Server Error