3.7 KiB
3.7 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Use Field Modifiers for Incremental Updates | HIGH | Atomic updates, prevents race conditions, cleaner code | sdk, modifiers, relations, files, numbers, atomic |
Use Field Modifiers for Incremental Updates
PocketBase supports + and - modifiers for incrementing numbers, appending/removing relation IDs, and managing file arrays without replacing the entire value.
Incorrect (read-modify-write pattern):
// Race condition: two users adding tags simultaneously
async function addTag(postId, newTagId) {
const post = await pb.collection('posts').getOne(postId);
const currentTags = post.tags || [];
// Another user might have added a tag in between!
await pb.collection('posts').update(postId, {
tags: [...currentTags, newTagId] // Might overwrite the other user's tag
});
}
// Inefficient for incrementing counters
async function incrementViews(postId) {
const post = await pb.collection('posts').getOne(postId);
await pb.collection('posts').update(postId, {
views: post.views + 1 // Extra read, race condition
});
}
Correct (using field modifiers):
// Atomic relation append with + modifier
async function addTag(postId, newTagId) {
await pb.collection('posts').update(postId, {
'tags+': newTagId // Appends to existing tags atomically
});
}
// Append multiple relations
async function addTags(postId, tagIds) {
await pb.collection('posts').update(postId, {
'tags+': tagIds // Appends array of IDs
});
}
// Prepend relations (+ prefix)
async function prependTag(postId, tagId) {
await pb.collection('posts').update(postId, {
'+tags': tagId // Prepends to start of array
});
}
// Remove relations with - modifier
async function removeTag(postId, tagId) {
await pb.collection('posts').update(postId, {
'tags-': tagId // Removes specific tag
});
}
// Remove multiple relations
async function removeTags(postId, tagIds) {
await pb.collection('posts').update(postId, {
'tags-': tagIds // Removes all specified tags
});
}
// Atomic number increment
async function incrementViews(postId) {
await pb.collection('posts').update(postId, {
'views+': 1 // Atomic increment, no race condition
});
}
// Atomic number decrement
async function decrementStock(productId, quantity) {
await pb.collection('products').update(productId, {
'stock-': quantity // Atomic decrement
});
}
// File append (for multi-file fields)
async function addImage(albumId, newImage) {
await pb.collection('albums').update(albumId, {
'images+': newImage // Appends new file to existing
});
}
// File removal
async function removeImage(albumId, filename) {
await pb.collection('albums').update(albumId, {
'images-': filename // Removes specific file by name
});
}
// Combined modifiers in single update
async function updatePost(postId, data) {
await pb.collection('posts').update(postId, {
title: data.title, // Replace field
'views+': 1, // Increment number
'tags+': data.newTagId, // Append relation
'tags-': data.oldTagId, // Remove relation
'images+': data.newImage // Append file
});
}
Modifier reference:
| Modifier | Field Types | Description |
|---|---|---|
field+ or +field |
relation, file | Append/prepend to array |
field- |
relation, file | Remove from array |
field+ |
number | Increment by value |
field- |
number | Decrement by value |
Benefits:
- Atomic: No read-modify-write race conditions
- Efficient: Single request, no extra read needed
- Clean: Expresses intent clearly
Note: Modifiers only work with update(), not create().
Reference: PocketBase Relations