PUT /api/v1/videos/id/{video_id} - Update Video Metadata
Overview
This endpoint allows a video owner (or platform moderator) to update a video's title, description, and tags. It demonstrates partial updates in Cassandra using $set semantics — only the fields included in the request are changed; others remain untouched.
Why it exists: Creators need to fix typos, add descriptions, and refine tags after upload. Moderators need to correct problematic content without deleting videos entirely.
HTTP Details
- Method: PUT
- Path:
/api/v1/videos/id/{video_id} - Auth Required: Yes — must be the video owner OR have moderator role
- Success Status: 200 OK
Path Parameters
| Parameter | Type | Description |
|---|---|---|
video_id |
UUID | The video to update |
Request Body
All fields are optional. Include only the fields you want to change.
{
"title": "Updated: Introduction to Apache Cassandra 5.0",
"description": "A comprehensive guide to Cassandra 5.0 features including SAI, vector search, and more.",
"tags": ["cassandra", "cassandra5", "nosql", "databases"]
}
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | No | New video title |
description |
string | No | New video description |
tags |
array of string | No | Replaces the entire tag set |
Response Body (200 OK)
{
"videoId": "550e8400-e29b-41d4-a716-446655440000",
"userId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Updated: Introduction to Apache Cassandra 5.0",
"description": "A comprehensive guide to Cassandra 5.0 features...",
"location": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"tags": ["cassandra", "cassandra5", "nosql", "databases"],
"previewImageLocation": "https://img.youtube.com/vi/.../mqdefault.jpg",
"addedDate": "2025-10-31T10:30:00Z",
"views": 1042,
"status": "READY"
}
Cassandra Concepts Explained
Partial Updates with $set
In Cassandra (and the Data API), updating a row does not require you to provide all columns. You can update exactly the columns you want to change. This is the default behavior — Cassandra stores columns independently within a row.
-- Only update name and description; tags and other columns are untouched
UPDATE killrvideo.videos
SET name = 'Updated Title',
description = 'New description'
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
In the Data API, this is expressed with $set:
{
"$set": {
"name": "Updated Title",
"description": "New description"
}
}
Why this is important: Unlike an SQL UPDATE, Cassandra does not touch columns that are omitted. Each column has its own cell with its own timestamp. Updating name does not affect description at all — they are independent writes.
Replacing a set<text> Collection
For the tags column (type set<text>), providing a new tag array in the request replaces the entire set. Cassandra handles this as a tombstone + new write under the hood.
-- Replace all tags
UPDATE killrvideo.videos
SET tags = {'cassandra', 'cassandra5', 'nosql'}
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
-- Alternatively, add specific tags
UPDATE killrvideo.videos
SET tags = tags + {'cassandra5'}
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
-- Remove a specific tag
UPDATE killrvideo.videos
SET tags = tags - {'old-tag'}
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
The API endpoint uses full replacement (you specify the complete desired tag set).
Authorization: Owner or Moderator
This endpoint enforces authorization at the application layer:
- Owner check: Does the authenticated user's
userIdmatch the video'suserid? - Moderator check: Does the authenticated user have the
moderatorrole in their JWT claims?
Either condition allows the update. This is a common RBAC (Role-Based Access Control) pattern:
Allow if: user.id == video.userid OR user.role == 'moderator'
Cassandra itself has no concept of row-level authorization — this logic lives entirely in the application.
Data Model
Table: videos (relevant columns for update)
CREATE TABLE killrvideo.videos (
videoid uuid PRIMARY KEY,
userid uuid, -- Owner check: must match authenticated user
name text, -- Updateable
description text, -- Updateable
tags set<text>, -- Updateable (full replacement)
-- ... other columns unchanged by this endpoint
);
Database Queries
1. Read Video to Check Ownership
Before updating, the service fetches the video to verify the requester is the owner (unless they're a moderator):
Equivalent CQL:
SELECT videoid, userid, name, description, tags, status
FROM killrvideo.videos
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
2. Apply Partial Update
Equivalent CQL (updating only changed fields):
UPDATE killrvideo.videos
SET name = 'Updated: Introduction to Apache Cassandra 5.0',
description = 'A comprehensive guide to Cassandra 5.0...',
tags = {'cassandra', 'cassandra5', 'nosql', 'databases'}
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
3. Read Updated Video (for response)
After the update, the service re-fetches the full row to return the current state:
SELECT * FROM killrvideo.videos
WHERE videoid = 550e8400-e29b-41d4-a716-446655440000;
Implementation Flow
┌──────────────────────────────────────────────────────────┐
│ 1. Client sends PUT /api/v1/videos/id/{video_id} │
│ with JWT and request body │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 2. Authenticate: extract userId and role from JWT │
│ └─ Missing/invalid JWT? → 401 Unauthorized │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 3. Validate request body (all fields optional) │
│ └─ Invalid types? → 422 Validation Error │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 4. Fetch video by videoid │
│ ├─ Not found? → 404 Video not found │
│ └─ Found? → Continue │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 5. Authorization check │
│ ├─ user.id == video.userid? → authorized │
│ ├─ user.role == 'moderator'? → authorized │
│ └─ Neither? → 403 Forbidden │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 6. Build $set update from non-null request fields │
│ UPDATE videos SET name=?, description=?, tags=? │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 7. Re-fetch video for response │
│ SELECT * FROM videos WHERE videoid = ? │
└────────────────────┬─────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 8. Return 200 OK with VideoDetailResponse │
└──────────────────────────────────────────────────────────┘
Total queries: 1 SELECT (ownership check) + 1 UPDATE + 1 SELECT (response) Expected latency: 15–30ms
Special Notes
1. Read-Modify-Write Is Not Atomic
The ownership check (SELECT) and the update (UPDATE) are two separate operations. In theory, another request could change the video between these two operations. For typical metadata updates, this race condition is inconsequential.
If strict consistency were required, a Cassandra Lightweight Transaction (LWT) could enforce it:
UPDATE killrvideo.videos
SET name = 'New Title'
WHERE videoid = ?
IF userid = ?; -- Only update if userid matches
LWTs add latency but provide atomic check-and-set.
2. The location Field Is Not Updateable
The YouTube URL (location) is intentionally excluded from this endpoint. Changing the URL would make the video a fundamentally different piece of content. If a creator wants to update to a different video, they should submit a new one.
3. Tag Set Semantics
When you send "tags": ["cassandra", "databases"], you are replacing the entire set, not appending. If the video previously had tags ["cassandra", "nosql", "old-tag"], after the update the tags will be exactly ["cassandra", "databases"].
If you want to add a single tag without removing others, you'd need to first GET the current tags, merge them in client code, and then PUT the merged set.
4. Cascading Updates
The name and tags columns exist in the videos table (source of truth). However, the latest_videos table also stores name for display in the home feed. Updating a video's title does not automatically update latest_videos in the current implementation — the home feed may show stale titles until the next cache refresh.
Developer Tips
Common Pitfalls
-
Forgetting to include unchanged tags: If you only want to add one tag, you must include all existing tags plus the new one. Sending just the new tag removes all others.
-
Assuming PUT replaces the whole record: This endpoint performs a partial update — only the fields you send are changed. Omitting
descriptiondoes not clear it. -
Not re-fetching after update: The response includes the latest state. Don't use the request body as the "new state" — the response is authoritative.
-
Authorization bypass via UUID guessing: UUIDs are not secret. Authorization checks are essential.
Best Practices
-
Normalize tags on update too: Apply the same lowercase/trim normalization used during video creation.
-
Return the full updated document: Clients should update their local state from the response, not from the request body.
-
Audit log moderator changes: When a moderator (not the owner) changes a video's metadata, log the action with who changed what and when.
-
Validate tag count and length: Same constraints as on creation — max 10 tags, max 32 chars each.
Performance Expectations
| Operation | Latency | Notes |
|---|---|---|
| Ownership check SELECT | 5ms | Partition key lookup |
| UPDATE | 5ms | Partition key write |
| Response SELECT | 5ms | Partition key lookup |
| Total round-trip | 15–25ms | Three sequential DB calls |
Related Endpoints
- GET /api/v1/videos/{id} - Read current state before updating
- GET /api/v1/videos/by-tag/{tag} - Browse videos by the tags you've set
- GET /api/v1/videos/{id}/status - Check video processing status