Initial commit
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
---
|
||||
title: Use getFirstListItem for Single Record Lookups
|
||||
impact: MEDIUM
|
||||
impactDescription: Cleaner code, automatic error handling for not found
|
||||
tags: query, lookup, find, getFirstListItem
|
||||
---
|
||||
|
||||
## Use getFirstListItem for Single Record Lookups
|
||||
|
||||
Use `getFirstListItem()` when you need to find a single record by a field value other than ID. It's cleaner than `getList()` with limit 1 and provides proper error handling.
|
||||
|
||||
**Incorrect (manual single-record lookup):**
|
||||
|
||||
```javascript
|
||||
// Using getList with limit 1 - verbose
|
||||
async function findUserByEmail(email) {
|
||||
const result = await pb.collection('users').getList(1, 1, {
|
||||
filter: pb.filter('email = {:email}', { email })
|
||||
});
|
||||
|
||||
if (result.items.length === 0) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
return result.items[0];
|
||||
}
|
||||
|
||||
// Using getFullList then filtering - wasteful
|
||||
async function findUserByUsername(username) {
|
||||
const users = await pb.collection('users').getFullList({
|
||||
filter: pb.filter('username = {:username}', { username })
|
||||
});
|
||||
return users[0]; // Might be undefined!
|
||||
}
|
||||
|
||||
// Fetching by ID when you have a different identifier
|
||||
async function findProductBySku(sku) {
|
||||
// Wrong: getOne expects the record ID
|
||||
const product = await pb.collection('products').getOne(sku); // Fails!
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (using getFirstListItem):**
|
||||
|
||||
```javascript
|
||||
// Clean single-record lookup by any field
|
||||
async function findUserByEmail(email) {
|
||||
try {
|
||||
const user = await pb.collection('users').getFirstListItem(
|
||||
pb.filter('email = {:email}', { email })
|
||||
);
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
return null; // Not found
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup by unique field
|
||||
async function findProductBySku(sku) {
|
||||
return pb.collection('products').getFirstListItem(
|
||||
pb.filter('sku = {:sku}', { sku })
|
||||
);
|
||||
}
|
||||
|
||||
// Lookup with expand
|
||||
async function findOrderByNumber(orderNumber) {
|
||||
return pb.collection('orders').getFirstListItem(
|
||||
pb.filter('orderNumber = {:num}', { num: orderNumber }),
|
||||
{ expand: 'customer,items' }
|
||||
);
|
||||
}
|
||||
|
||||
// Complex filter conditions
|
||||
async function findActiveSubscription(userId) {
|
||||
return pb.collection('subscriptions').getFirstListItem(
|
||||
pb.filter(
|
||||
'user = {:userId} && status = "active" && expiresAt > @now',
|
||||
{ userId }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// With field selection
|
||||
async function getUserIdByEmail(email) {
|
||||
const user = await pb.collection('users').getFirstListItem(
|
||||
pb.filter('email = {:email}', { email }),
|
||||
{ fields: 'id' }
|
||||
);
|
||||
return user.id;
|
||||
}
|
||||
```
|
||||
|
||||
**Comparison with getOne:**
|
||||
|
||||
```javascript
|
||||
// getOne - fetch by record ID
|
||||
const post = await pb.collection('posts').getOne('abc123');
|
||||
|
||||
// getFirstListItem - fetch by any filter (use pb.filter for safe binding)
|
||||
const post = await pb.collection('posts').getFirstListItem(
|
||||
pb.filter('slug = {:slug}', { slug: 'hello-world' })
|
||||
);
|
||||
const user = await pb.collection('users').getFirstListItem(
|
||||
pb.filter('username = {:name}', { name: 'john' })
|
||||
);
|
||||
const order = await pb.collection('orders').getFirstListItem(
|
||||
pb.filter('orderNumber = {:num}', { num: 12345 })
|
||||
);
|
||||
```
|
||||
|
||||
**Error handling:**
|
||||
|
||||
```javascript
|
||||
// getFirstListItem throws 404 if no match found
|
||||
try {
|
||||
const user = await pb.collection('users').getFirstListItem(
|
||||
pb.filter('email = {:email}', { email })
|
||||
);
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
// No matching record - handle appropriately
|
||||
return null;
|
||||
}
|
||||
// Other error (network, auth, etc.)
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Wrapper function for optional lookup
|
||||
async function findFirst(collection, filter, options = {}) {
|
||||
try {
|
||||
return await pb.collection(collection).getFirstListItem(filter, options);
|
||||
} catch (error) {
|
||||
if (error.status === 404) return null;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const user = await findFirst('users', pb.filter('email = {:e}', { e: email }));
|
||||
if (!user) {
|
||||
console.log('User not found');
|
||||
}
|
||||
```
|
||||
|
||||
**When to use each method:**
|
||||
|
||||
| Method | Use When |
|
||||
|--------|----------|
|
||||
| `getOne(id)` | You have the record ID |
|
||||
| `getFirstListItem(filter)` | Finding by unique field (email, slug, sku) |
|
||||
| `getList(1, 1, { filter })` | Need pagination metadata |
|
||||
| `getFullList({ filter })` | Expecting multiple results |
|
||||
|
||||
Reference: [PocketBase Records API](https://pocketbase.io/docs/api-records/)
|
||||
Reference in New Issue
Block a user