Overview
The collections module provides server actions for organizing prompts into user-defined groups. Collections help users categorize and manage related prompts together. All actions require authentication.
createCollection
Creates a new collection owned by the authenticated user.
Location: src/features/collections/actions.ts:8
export async function createCollection(
input: CreateCollectionInput
): Promise<{
data?: Collection;
error?: string;
details?: ZodFormattedError;
}>
Parameters
input
CreateCollectionInput
required
Collection data to createCollection name (1-50 characters)
Optional description (max 200 characters)
Returns
The newly created collection object (returned on success) Error message if creation failed
Detailed validation errors if input was invalid
Validation
Input is validated using the createCollectionSchema Zod schema:
const createCollectionSchema = z.object({
name: z.string().min(1, "Name is required").max(50, "Name is too long"),
description: z.string().max(200, "Description is too long").optional(),
});
Example
import { createCollection } from '@/features/collections/actions';
const result = await createCollection({
name: 'Code Review Prompts',
description: 'Templates for reviewing pull requests and code quality',
});
if (result.error) {
console.error('Failed:', result.error);
if (result.details) {
console.error('Validation errors:', result.details);
}
} else if (result.data) {
console.log('Created collection:', result.data.id);
}
Behavior
- Automatically assigns the authenticated user as the owner
- Returns
{ error: "Unauthorized" } if user is not logged in
- Revalidates the
/prompts page cache after creation
getCollections
Retrieves all collections accessible to the authenticated user.
Location: src/features/collections/actions.ts:41
export async function getCollections(): Promise<{
data: Collection[];
}>
Parameters
None
Returns
Array of collection objects, ordered alphabetically by name. Returns empty array [] if error occurs or no collections exist.
Example
import { getCollections } from '@/features/collections/actions';
const { data } = await getCollections();
console.log(`You have ${data.length} collections`);
data.forEach(collection => {
console.log(`- ${collection.name}`);
});
Behavior
- Returns collections ordered by
name (ascending)
- Respects Row-Level Security (RLS) policies - users only see their own collections
- Returns empty array on database errors (errors are logged to console)
updateCollection
Updates the name and/or description of an existing collection.
Location: src/features/collections/actions.ts:58
export async function updateCollection(
id: string,
input: CreateCollectionInput
): Promise<{
error: string | null;
details?: ZodFormattedError;
}>
Parameters
UUID of the collection to update
input
CreateCollectionInput
required
Updated collection dataNew collection name (1-50 characters)
New description (max 200 characters)
Returns
null if update succeeded, error message otherwise
Detailed validation errors if input was invalid
Example
import { updateCollection } from '@/features/collections/actions';
const result = await updateCollection('collection-uuid', {
name: 'Updated Collection Name',
description: 'New description',
});
if (result.error) {
console.error('Update failed:', result.error);
} else {
console.log('Collection updated successfully');
}
Behavior
- Updates
name, description, and updated_at fields
- The
updated_at timestamp is set to the current time
- Revalidates the
/prompts page cache after update
- RLS policies ensure users can only update their own collections
deleteCollection
Permanently deletes a collection.
Location: src/features/collections/actions.ts:84
export async function deleteCollection(
id: string
): Promise<{ error: string | null }>
Parameters
UUID of the collection to delete
Returns
null if deletion succeeded, error message otherwise
Example
import { deleteCollection } from '@/features/collections/actions';
const result = await deleteCollection('collection-uuid');
if (result.error) {
console.error('Delete failed:', result.error);
} else {
console.log('Collection deleted');
}
Deleting a collection removes it permanently but does NOT delete the prompts within it. Prompts are only removed from the collection via the collection_prompts join table.
Behavior
- Permanently removes the collection record
- Cascade deletes entries in
collection_prompts join table (handled by database foreign key constraints)
- Prompts themselves are preserved
- Revalidates the
/prompts page cache after deletion
- RLS policies ensure users can only delete their own collections
addToCollection
Adds a prompt to a collection.
Location: src/features/collections/actions.ts:101
export async function addToCollection(
promptId: string,
collectionId: string
): Promise<{ error: string | null }>
Parameters
UUID of the prompt to add
UUID of the collection to add the prompt to
Returns
null if addition succeeded, error message otherwise
Example
import { addToCollection } from '@/features/collections/actions';
const result = await addToCollection(
'prompt-uuid',
'collection-uuid'
);
if (result.error) {
console.error('Failed to add prompt:', result.error);
} else {
console.log('Prompt added to collection');
}
Behavior
- Creates a record in the
collection_prompts join table
- A prompt can belong to multiple collections
- If the prompt is already in the collection, the database will return an error (unique constraint violation)
- Revalidates the
/prompts page cache after addition
- RLS policies ensure users can only add prompts to their own collections
removeFromCollection
Removes a prompt from a collection.
Location: src/features/collections/actions.ts:120
export async function removeFromCollection(
promptId: string,
collectionId: string
): Promise<{ error: string | null }>
Parameters
UUID of the prompt to remove
UUID of the collection to remove the prompt from
Returns
null if removal succeeded, error message otherwise
Example
import { removeFromCollection } from '@/features/collections/actions';
const result = await removeFromCollection(
'prompt-uuid',
'collection-uuid'
);
if (result.error) {
console.error('Failed to remove prompt:', result.error);
} else {
console.log('Prompt removed from collection');
}
Behavior
- Deletes the record from the
collection_prompts join table
- Does NOT delete the prompt itself
- If the prompt is not in the collection, the operation succeeds silently (no error)
- Revalidates the
/prompts page cache after removal
- RLS policies ensure users can only modify their own collections
Authentication
All actions require authentication. The pattern used is:
const cookieStore = await cookies();
const supabase = createClient(cookieStore);
const { data: { user } } = await supabase.auth.getUser();
if (!user) {
return { error: "Unauthorized" };
}
Error Handling
Actions follow a consistent error response pattern:
if (error) {
return { error: error.message };
}
Common Errors
"Unauthorized" - User is not logged in
"Invalid input" - Validation failed (check details field for specifics)
"Name is required" - Collection name is empty
"Name is too long" - Collection name exceeds 50 characters
"Description is too long" - Description exceeds 200 characters
- Database constraint violations (e.g., duplicate prompt in collection)
Cache Revalidation
All mutating actions call revalidatePath("/prompts") to ensure the UI immediately reflects changes. This invalidates Next.js cache for the prompts list page.
Database Schema
Collections use two tables:
collections
CREATE TABLE collections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name VARCHAR(50) NOT NULL,
description VARCHAR(200),
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
collection_prompts (join table)
CREATE TABLE collection_prompts (
collection_id UUID REFERENCES collections(id) ON DELETE CASCADE,
prompt_id UUID REFERENCES prompts(id) ON DELETE CASCADE,
PRIMARY KEY (collection_id, prompt_id)
);
Security
- Row-Level Security (RLS) policies ensure users can only access their own collections
- All actions inherit the authenticated user’s context from the Supabase client
- Foreign key constraints prevent orphaned data
- Cascade deletes ensure referential integrity
Usage Example: Full Collection Workflow
import {
createCollection,
addToCollection,
getCollections,
removeFromCollection,
deleteCollection,
} from '@/features/collections/actions';
// 1. Create a collection
const createResult = await createCollection({
name: 'API Prompts',
description: 'Prompts for API documentation and testing',
});
if (createResult.data) {
const collectionId = createResult.data.id;
// 2. Add prompts to the collection
await addToCollection('prompt-1-uuid', collectionId);
await addToCollection('prompt-2-uuid', collectionId);
// 3. List all collections
const { data: collections } = await getCollections();
console.log('Collections:', collections);
// 4. Remove a prompt from the collection
await removeFromCollection('prompt-1-uuid', collectionId);
// 5. Delete the collection (prompts are preserved)
await deleteCollection(collectionId);
}