Skip to content

Commit 21b7aed

Browse files
committed
Update README.md to reflect Meteor 3 compatibility, async hook support, and limitations for find hooks.
1 parent c1614fa commit 21b7aed

File tree

1 file changed

+122
-19
lines changed

1 file changed

+122
-19
lines changed

README.md

Lines changed: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
![CodeQL Analysis](https://github.com/Meteor-Community-Packages/meteor-collection-hooks/workflows/CodeQL/badge.svg)
66

77

8-
Extends Mongo.Collection with `before`/`after` hooks for `insert`, `update`, `remove`, `find`, and `findOne`.
8+
Extends Mongo.Collection with `before`/`after` hooks for `insert`, `update`, `remove`, `upsert`, `find`, and `findOne`. **Meteor 2.16+ & 3.x compatible with async hook support.**
99

1010
Works across client, server or a mix. Also works when a client initiates a collection method and the server runs the hook, all while respecting the collection validators (allow/deny).
1111

@@ -19,6 +19,67 @@ Installation:
1919
meteor add matb33:collection-hooks
2020
```
2121

22+
## Meteor 3 Compatibility & Async Hooks
23+
24+
**Meteor Version Support:** 2.16+ through 3.1+
25+
26+
**Important Behavioral Changes in Meteor 3:**
27+
28+
### Async Hooks Support
29+
As of v2.0.0, most hooks support async functions:
30+
31+
```javascript
32+
// ✅ Async hooks work for these operations
33+
collection.before.insert(async function(userId, doc) {
34+
await validateDoc(doc);
35+
});
36+
37+
collection.after.update(async function(userId, doc, fieldNames, modifier, options) {
38+
await notifyExternalService(doc);
39+
});
40+
```
41+
42+
### Find Hooks Limitations
43+
Due to Meteor 3's synchronous `find()` method, find hooks have specific limitations:
44+
45+
```javascript
46+
// ✅ WORKS: Sync before.find hooks
47+
collection.before.find(function(userId, selector, options) {
48+
selector.deletedAt = { $exists: false }; // Modify selector
49+
});
50+
51+
// ❌ THROWS ERROR: Async before.find hooks
52+
collection.before.find(async function(userId, selector, options) {
53+
// This will throw: "Cannot use async function as before.find hook"
54+
});
55+
56+
// ✅ WORKS: after.find hooks (sync and async)
57+
collection.after.find(async function(userId, selector, options, cursor) {
58+
await logFindOperation(selector);
59+
});
60+
```
61+
62+
### Hook Trigger Conditions
63+
64+
**findOne Hooks:**
65+
```javascript
66+
await collection.findOneAsync({}) // ✅ Triggers hooks
67+
collection.findOne({}) // ❌ No hooks triggered
68+
```
69+
70+
**find Hooks:**
71+
```javascript
72+
// ✅ These trigger find hooks:
73+
const cursor = collection.find({});
74+
await cursor.fetchAsync(); // ✅ Hooks fire
75+
await cursor.countAsync(); // ✅ Hooks fire
76+
await cursor.forEachAsync(); // ✅ Hooks fire
77+
78+
// ❌ These DON'T trigger find hooks:
79+
collection.find({}).fetch(); // ❌ No hooks
80+
collection.find({}).count(); // ❌ No hooks
81+
```
82+
2283
--------------------------------------------------------------------------------
2384

2485
### .before.insert(userId, doc)
@@ -185,64 +246,100 @@ test.after.remove(function (userId, doc) {
185246

186247
### .before.find(userId, selector, options)
187248

188-
Fired before a find query.
249+
Fired before a find query. **Meteor 3 Limitation: Cannot be async.**
189250

190251
Allows you to adjust selector/options on-the-fly.
191252

192253
```javascript
193254
test.before.find(function (userId, selector, options) {
194-
// ...
255+
// ✅ Sync operations only
256+
selector.deletedAt = { $exists: false };
257+
});
258+
259+
// ❌ This will throw an error:
260+
test.before.find(async function (userId, selector, options) {
261+
// Error: "Cannot use async function as before.find hook"
195262
});
196263
```
197264

198265
__Important:__
199-
- The function used as `before.find` hook cannot be async
200-
- This hook does not get called for `after.update` hooks (see https://github.com/Meteor-Community-Packages/meteor-collection-hooks/pull/297).
266+
- The function used as `before.find` hook **cannot be async** (throws error)
267+
- This hook does not get called for `after.update` hooks (see https://github.com/Meteor-Community-Packages/meteor-collection-hooks/pull/297)
268+
- Only triggers when using cursor async methods (`fetchAsync()`, `countAsync()`, etc.)
201269

202270
--------------------------------------------------------------------------------
203271

204272
### .after.find(userId, selector, options, cursor)
205273

206-
Fired after a find query.
274+
Fired after a find query when using cursor async methods.
207275

208-
Allows you to act on a given find query. The cursor resulting from
209-
the query is provided as the last argument for convenience.
276+
Allows you to act on a given find query. Both sync and async functions are supported.
210277

211278
```javascript
279+
// ✅ Sync after.find
212280
test.after.find(function (userId, selector, options, cursor) {
213-
// ...
281+
logOperation(selector);
282+
});
283+
284+
// ✅ Async after.find
285+
test.after.find(async function (userId, selector, options, cursor) {
286+
await logToExternalService(selector);
214287
});
215288
```
216289

290+
**Triggers only on cursor async methods:**
291+
```javascript
292+
const cursor = collection.find({});
293+
await cursor.fetchAsync(); // ✅ Triggers after.find
294+
await cursor.countAsync(); // ✅ Triggers after.find
295+
cursor.fetch(); // ❌ No hooks triggered
296+
```
297+
217298
--------------------------------------------------------------------------------
218299

219300
### .before.findOne(userId, selector, options)
220301

221-
Fired before a findOne query.
222-
223-
Allows you to adjust selector/options on-the-fly.
302+
Fired before a findOne query. **Supports async functions.**
224303

225304
```javascript
305+
// ✅ Sync before.findOne
226306
test.before.findOne(function (userId, selector, options) {
227-
// ...
307+
selector.status = 'active';
228308
});
309+
310+
// ✅ Async before.findOne
311+
test.before.findOne(async function (userId, selector, options) {
312+
await enrichSelector(selector);
313+
// Return false to abort the operation
314+
return false;
315+
});
316+
```
317+
318+
**Only triggers on async methods:**
319+
```javascript
320+
await collection.findOneAsync({}) // ✅ Triggers hooks
321+
collection.findOne({}) // ❌ No hooks triggered
229322
```
230323

231324
--------------------------------------------------------------------------------
232325

233326
### .after.findOne(userId, selector, options, doc)
234327

235-
Fired after a findOne query.
236-
237-
Allows you to act on a given findOne query. The document resulting
238-
from the query is provided as the last argument for convenience.
328+
Fired after a findOne query. **Supports async functions.**
239329

240330
```javascript
241-
test.after.findOne(function (userId, selector, options, doc) {
242-
// ...
331+
// ✅ Async after.findOne
332+
test.after.findOne(async function (userId, selector, options, doc) {
333+
await processDocument(doc);
243334
});
244335
```
245336

337+
**Only triggers on async methods:**
338+
```javascript
339+
await collection.findOneAsync({}) // ✅ Triggers hooks
340+
collection.findOne({}) // ❌ No hooks triggered
341+
```
342+
246343
--------------------------------------------------------------------------------
247344

248345
## Direct access (circumventing hooks)
@@ -353,6 +450,12 @@ server.*
353450

354451
- `find` hooks are also fired when fetching documents for `update`, `upsert` and `remove` hooks.
355452

453+
- **Meteor 3 Behavior:** Find hooks only trigger on async cursor methods (`fetchAsync()`, `countAsync()`, etc.). Sync methods (`fetch()`, `count()`) do not trigger hooks.
454+
455+
- **findOne Behavior:** findOne hooks only trigger on `findOneAsync()`. The sync `findOne()` method does not trigger hooks in Meteor 3.
456+
457+
- `before.find` hooks cannot be async and will throw an error if an async function is provided.
458+
356459
- If using the `direct` version to bypass a hook, any mongo operations done within nested
357460
callbacks of the `direct` operation will also by default run as `direct`. You can use the following
358461
line in a nested callback before the operation to unset the `direct` setting:

0 commit comments

Comments
 (0)