Skip to content

Commit 1bc9080

Browse files
authored
Merge pull request #11 from r-el/feature/async-api
Add Promise/Async API Support
2 parents 83d7cdd + 78b010c commit 1bc9080

File tree

8 files changed

+415
-79
lines changed

8 files changed

+415
-79
lines changed

CHANGELOG.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,30 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
65
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
76
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
87

8+
## [1.3.0] - 2025-09-11
9+
10+
### Added
11+
12+
- **Promise/Async Support** - Full async/await API alongside callback methods
13+
- All CRUD methods now have async variants (createAsync, readAllAsync, etc.)
14+
- Automatic promisification wrapper for callback-based methods
15+
- Complete TypeScript definitions for async methods
16+
- New async usage examples and comprehensive test coverage
17+
- **Enhanced Examples** - Added async/await usage demonstration
18+
919
## [1.2.0] - 2025-07-08
1020

1121
### Added
12-
- **TypeScript Definitions** - Bundled `lib/json-file-crud.d.ts` for IDE support
1322

23+
- **TypeScript Definitions** - Bundled `lib/json-file-crud.d.ts` for IDE support
1424

1525
## [1.1.0] - 2025-07-07
1626

1727
### Added
28+
1829
- **Unique Fields Support** - Prevent duplicate values in specified fields
1930
- Configure unique fields via `uniqueFields` option
2031
- Automatic validation on create and update operations
@@ -33,6 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3344
- No need to manually create directories before using the library
3445

3546
### Enhanced
47+
3648
- **Test Suite Reorganization** - Improved test structure
3749
- Split tests into logical files by functionality
3850
- `test-basic.js` - Basic functionality and convenience features
@@ -45,10 +57,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4557
- Backward compatible with existing code
4658

4759
### Changed
60+
4861
- Package description updated to reflect new features
4962
- Test scripts updated for reorganized test structure
5063

5164
### Technical Details
65+
5266
- All new features maintain backward compatibility
5367
- Thread-safe operations through existing queue system
5468
- Comprehensive error handling for all new features
@@ -57,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5771
## [1.0.0] - 2025-07-07
5872

5973
### Added
74+
6075
- Initial release of JsonFileCRUD
6176
- Complete CRUD operations (Create, Read, Update, Delete)
6277
- Auto-ID assignment with duplicate prevention
@@ -74,6 +89,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7489
- Contributing guidelines with improvement ideas
7590

7691
### Features
92+
7793
- **create(item, callback)** - Create new items with auto-ID
7894
- **readAll(callback)** - Read all items from file
7995
- **findById(id, callback)** - Find item by ID
@@ -84,12 +100,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
84100
- **writeAll(items, callback)** - Replace all data
85101

86102
### Performance
103+
87104
- Optimized for small to medium datasets (up to ~10,000 items)
88105
- Sequential operations prevent race conditions
89106
- Automatic file creation on first write
90107
- Memory-efficient data handling
91108

92109
### Documentation
110+
93111
- Complete README with API reference
94112
- Multiple practical examples
95113
- Error handling guide
@@ -99,6 +117,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99117
## [Unreleased]
100118

101119
### Future Improvements
120+
102121
- Promise-based API (async/await)
103122
- Batch operations (createMany, updateMany, deleteMany)
104123
- File locking for multi-process safety

README.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ A simple, robust, and thread-safe CRUD library for managing JSON objects in file
55
## Features
66

77
- **Simple API** - Easy to use CRUD operations
8+
- **Promise/Async Support** - Full async/await API alongside callback methods ✨ *New in v1.3*
89
- **Thread-safe** - Sequential operations with automatic queuing
910
- **Auto-ID assignment** - Automatic ID generation for new items (configurable)
1011
- **Unique Fields** - Prevent duplicate values in specified fields ✨ *New in v1.1*
@@ -74,6 +75,27 @@ db.delete(1, (err, deletedItem) => {
7475
});
7576
```
7677

78+
### Async/Await Usage
79+
80+
```javascript
81+
// Same operations using async/await
82+
try {
83+
const user = await db.createAsync({ name: 'John', age: 30 });
84+
console.log('Created:', user);
85+
86+
const foundUser = await db.findByIdAsync(user.id);
87+
console.log('Found:', foundUser);
88+
89+
const updatedUser = await db.updateAsync(user.id, { age: 31 });
90+
console.log('Updated:', updatedUser);
91+
92+
const deletedUser = await db.deleteAsync(user.id);
93+
console.log('Deleted:', deletedUser);
94+
} catch (error) {
95+
console.error('Error:', error.message);
96+
}
97+
```
98+
7799
## API Reference
78100

79101
### Constructor
@@ -248,6 +270,33 @@ db.writeAll(newData, (err) => {
248270
});
249271
```
250272

273+
### Promise-based API
274+
275+
All methods have async counterparts that return Promises. Simply add `Async` to the method name:
276+
277+
#### Async Method Names
278+
279+
All callback methods have async counterparts. Add `Async` to the method name:
280+
281+
```javascript
282+
// Using async/await
283+
try {
284+
const user = await db.createAsync({ name: 'John', age: 30 });
285+
const allUsers = await db.readAllAsync();
286+
const foundUser = await db.findByIdAsync(user.id);
287+
const updatedUser = await db.updateAsync(user.id, { age: 31 });
288+
await db.deleteAsync(user.id);
289+
} catch (error) {
290+
console.error('Operation failed:', error.message);
291+
}
292+
293+
// Using Promises
294+
db.createAsync({ name: 'Jane', age: 25 })
295+
.then(user => db.findByIdAsync(user.id))
296+
.then(foundUser => console.log('Found:', foundUser))
297+
.catch(error => console.error('Error:', error.message));
298+
```
299+
251300
## Advanced Features
252301

253302
### Unique Fields
@@ -297,6 +346,7 @@ For comprehensive examples, see the [examples](./examples/) directory:
297346
- **[Basic Usage](./examples/basic-usage.js)** - Simple CRUD operations
298347
- **[Advanced Features](./examples/advanced-usage.js)** - Concurrent operations, filtering, custom ID fields
299348
- **[User Management](./examples/user-management.js)** - Real-world application with unique fields validation
349+
- **[Async/Await Demo](./examples/async-demo.js)** - Promise-based API usage with async/await
300350

301351
### Quick Examples
302352

@@ -360,7 +410,6 @@ Contributions are welcome! Here are some ways you can help improve JsonFileCRUD:
360410

361411
### Ideas for Contributions
362412

363-
- **Async/Await Support**: Add Promise-based API alongside callbacks
364413
- **Batch Operations**: Add bulk insert/update/delete operations
365414
- **File Locking**: Add file locking for multi-process safety
366415
- **Enhanced Documentation**: Improve documentation and add more examples
@@ -377,4 +426,3 @@ Contributions are welcome! Here are some ways you can help improve JsonFileCRUD:
377426
## License
378427

379428
MIT License - see [LICENSE](./LICENSE) file for details.
380-

examples/async-demo.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Async/Await Usage Example
3+
*
4+
* This example demonstrates the new Promise-based API
5+
* alongside the traditional callback-based methods
6+
*/
7+
8+
import JsonFileCRUD from "../lib/json-file-crud.js";
9+
10+
const crud = new JsonFileCRUD("./examples/data/async-demo.json");
11+
12+
console.log("Async/Await API Demo\n");
13+
14+
async function asyncDemo() {
15+
try {
16+
// Clean start
17+
await crud.writeAllAsync([]);
18+
19+
// Create items using async/await
20+
console.log("Creating users with async/await...");
21+
const user1 = await crud.createAsync({ name: "Ariel", age: 30 });
22+
console.log("Created:", user1);
23+
24+
const user2 = await crud.createAsync({ name: "John", age: 25 });
25+
console.log("Created:", user2);
26+
27+
// Read all users
28+
console.log("\nReading all users...");
29+
const allUsers = await crud.readAllAsync();
30+
console.log("All users:", allUsers);
31+
32+
// Find by ID
33+
console.log("\nFinding user by ID...");
34+
const foundUser = await crud.findByIdAsync(user1.id);
35+
console.log("Found user:", foundUser);
36+
37+
// Update user
38+
console.log("\nUpdating user...");
39+
const updatedUser = await crud.updateAsync(user1.id, { age: 31 });
40+
console.log("Updated user:", updatedUser);
41+
42+
// Filter users
43+
console.log("\nFiltering users over 30...");
44+
const adults = await crud.findByAsync((user) => user.age > 30);
45+
console.log("Adults:", adults);
46+
47+
// Count users
48+
console.log("\nCounting users...");
49+
const count = await crud.countAsync();
50+
console.log("Total users:", count);
51+
52+
// Delete user
53+
console.log("\nDeleting user...");
54+
const deletedUser = await crud.deleteAsync(user2.id);
55+
console.log("Deleted user:", deletedUser);
56+
57+
// Final count
58+
const finalCount = await crud.countAsync();
59+
console.log("Final count:", finalCount);
60+
61+
console.log("\nAsync/Await demo completed successfully!");
62+
} catch (error) {
63+
console.error("Error in async demo:", error.message);
64+
}
65+
}
66+
67+
// Run the async demo
68+
asyncDemo();

lib/async-wrapper.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Async wrapper utilities for JsonFileCRUD
3+
* Provides Promise-based API alongside callback-based methods
4+
*/
5+
6+
/**
7+
* Converts a callback-based method to Promise-based
8+
* @param {Function} method - The callback-based method to wrap
9+
* @param {Object} context - The context (this) to bind to the method
10+
* @returns {Function} Promise-based version of the method
11+
*/
12+
export function promisify(method, context) {
13+
return function (...args) {
14+
return new Promise((resolve, reject) => {
15+
// Add callback that resolves or rejects the promise
16+
const callback = (error, result) => {
17+
if (error) {
18+
reject(error);
19+
} else {
20+
resolve(result);
21+
}
22+
};
23+
24+
// Call the original method with callback
25+
method.call(context, ...args, callback);
26+
});
27+
};
28+
}
29+
30+
/**
31+
* Creates async versions of all CRUD methods
32+
* @param {Object} crudInstance - Instance of JsonFileCRUD
33+
* @returns {Object} Object with async method variants
34+
*/
35+
export function createAsyncMethods(crudInstance) {
36+
return {
37+
// Create methods
38+
createAsync: promisify(crudInstance.create, crudInstance),
39+
40+
// Read methods
41+
readAllAsync: promisify(crudInstance.readAll, crudInstance),
42+
findByIdAsync: promisify(crudInstance.findById, crudInstance),
43+
findByAsync: promisify(crudInstance.findBy, crudInstance),
44+
countAsync: promisify(crudInstance.count, crudInstance),
45+
46+
// Update methods
47+
updateAsync: promisify(crudInstance.update, crudInstance),
48+
49+
// Delete methods
50+
deleteAsync: promisify(crudInstance.delete, crudInstance),
51+
deleteAllAsync: promisify(crudInstance.deleteAll, crudInstance),
52+
53+
// Utility methods
54+
writeAllAsync: promisify(crudInstance.writeAll, crudInstance),
55+
};
56+
}

lib/json-file-crud.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default class JsonFileCRUD<T = any> {
1414
readonly autoId: boolean;
1515
readonly uniqueFields: string[];
1616

17+
// Callback-based methods
1718
create(item: T, callback: Callback<T>): void;
1819
readAll(callback: Callback<T[]>): void;
1920
findById(id: any, callback: Callback<T>): void;
@@ -24,6 +25,17 @@ export default class JsonFileCRUD<T = any> {
2425
deleteAll(callback: (err: Error | null) => void): void;
2526
writeAll(items: T[], callback: (err: Error | null) => void): void;
2627
processWriteQueue(): void;
28+
29+
// Promise-based methods
30+
createAsync(item: T): Promise<T>;
31+
readAllAsync(): Promise<T[]>;
32+
findByIdAsync(id: any): Promise<T>;
33+
findByAsync(filterFn: (item: T) => boolean): Promise<T[]>;
34+
countAsync(): Promise<number>;
35+
updateAsync(id: any, data: Partial<T>): Promise<T>;
36+
deleteAsync(id: any): Promise<T>;
37+
deleteAllAsync(): Promise<void>;
38+
writeAllAsync(items: T[]): Promise<void>;
2739
}
2840

2941
export function createCrud<T = any>(filePath: string, options?: CrudOptions): JsonFileCRUD<T>;

0 commit comments

Comments
 (0)