|
1 | 1 | # JsonFileCRUD
|
2 | 2 |
|
3 |
| -CRUD operations for JSON files. |
| 3 | +A simple, robust, and thread-safe CRUD library for managing JSON objects in files using Node.js. |
4 | 4 |
|
5 |
| -## Install |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Simple API** - Easy to use CRUD operations |
| 8 | +- **Thread-safe** - Sequential operations with automatic queuing |
| 9 | +- **Auto-ID assignment** - Automatic ID generation for new items |
| 10 | +- **Configurable ID field** - Use any field name as the primary key |
| 11 | +- **Comprehensive error handling** - Detailed error messages and validation |
| 12 | +- **Zero dependencies** - Built with only Node.js built-in modules |
| 13 | +- **ESM support** - Full ES modules support |
| 14 | + |
| 15 | +## Installation |
6 | 16 |
|
7 | 17 | ```bash
|
8 | 18 | npm install json-file-crud
|
9 | 19 | ```
|
10 | 20 |
|
11 |
| -## Usage |
| 21 | +## Quick Start |
12 | 22 |
|
13 | 23 | ```javascript
|
14 | 24 | import JsonFileCRUD from 'json-file-crud';
|
15 | 25 |
|
16 | 26 | const db = new JsonFileCRUD('./data.json');
|
17 | 27 |
|
18 |
| -// Create |
| 28 | +// Create a new item |
19 | 29 | db.create({ name: 'John', age: 30 }, (err, result) => {
|
20 |
| - console.log('Created:', result); |
21 |
| -}); |
22 |
| - |
23 |
| -// Read by ID |
24 |
| -db.findById(1, (err, item) => { |
25 |
| - console.log('Item:', item); |
| 30 | + if (err) { |
| 31 | + console.error('Error:', err.message); |
| 32 | + return; |
| 33 | + } |
| 34 | + console.log('Created:', result); // { name: 'John', age: 30, id: 1 } |
26 | 35 | });
|
27 | 36 |
|
28 |
| -// Read all |
| 37 | +// Read all items |
29 | 38 | db.readAll((err, items) => {
|
| 39 | + if (err) throw err; |
30 | 40 | console.log('All items:', items);
|
31 | 41 | });
|
32 | 42 |
|
33 |
| -// Update |
34 |
| -db.update(1, { age: 31 }, (err, result) => { |
35 |
| - console.log('Updated:', result); |
| 43 | +// Find by ID |
| 44 | +db.findById(1, (err, item) => { |
| 45 | + if (err) throw err; |
| 46 | + console.log('Found:', item); |
| 47 | +}); |
| 48 | + |
| 49 | +// Update an item |
| 50 | +db.update(1, { age: 31 }, (err, updatedItem) => { |
| 51 | + if (err) throw err; |
| 52 | + console.log('Updated:', updatedItem); |
36 | 53 | });
|
37 | 54 |
|
38 |
| -// Delete |
| 55 | +// Delete an item |
39 | 56 | db.delete(1, (err, deletedItem) => {
|
| 57 | + if (err) throw err; |
40 | 58 | console.log('Deleted:', deletedItem);
|
41 | 59 | });
|
42 | 60 | ```
|
43 | 61 |
|
| 62 | +## API Reference |
| 63 | + |
| 64 | +### Constructor |
| 65 | + |
| 66 | +#### `new JsonFileCRUD(filePath, options)` |
| 67 | + |
| 68 | +Creates a new JsonFileCRUD instance. |
| 69 | + |
| 70 | +- `filePath` (string): Path to the JSON file |
| 71 | +- `options` (object, optional): |
| 72 | + - `idField` (string): Name of the ID field (default: 'id') |
| 73 | + |
| 74 | +```javascript |
| 75 | +// Default ID field |
| 76 | +const db = new JsonFileCRUD('./data.json'); |
| 77 | + |
| 78 | +// Custom ID field |
| 79 | +const products = new JsonFileCRUD('./products.json', { idField: 'productId' }); |
| 80 | +``` |
| 81 | + |
| 82 | +### CRUD Operations |
| 83 | + |
| 84 | +#### `create(item, callback)` |
| 85 | + |
| 86 | +Creates a new item. Auto-assigns an ID if not provided. |
| 87 | + |
| 88 | +- `item` (object): The item to create |
| 89 | +- `callback` (function): `(error, createdItem) => {}` |
| 90 | + |
| 91 | +```javascript |
| 92 | +db.create({ name: 'Alice', email: 'alice@example.com' }, (err, result) => { |
| 93 | + // result: { name: 'Alice', email: 'alice@example.com', id: 1 } |
| 94 | +}); |
| 95 | +``` |
| 96 | + |
| 97 | +#### `readAll(callback)` |
| 98 | + |
| 99 | +Reads all items from the file. |
| 100 | + |
| 101 | +- `callback` (function): `(error, items) => {}` |
| 102 | + |
| 103 | +```javascript |
| 104 | +db.readAll((err, items) => { |
| 105 | + // items: array of all items |
| 106 | +}); |
| 107 | +``` |
| 108 | + |
| 109 | +#### `findById(id, callback)` |
| 110 | + |
| 111 | +Finds an item by its ID. |
| 112 | + |
| 113 | +- `id` (any): The ID to search for |
| 114 | +- `callback` (function): `(error, item) => {}` |
| 115 | + |
| 116 | +```javascript |
| 117 | +db.findById(1, (err, item) => { |
| 118 | + // item: the found item or null if not found |
| 119 | +}); |
| 120 | +``` |
| 121 | + |
| 122 | +#### `findBy(filterFunction, callback)` |
| 123 | + |
| 124 | +Finds items that match a filter function. |
| 125 | + |
| 126 | +- `filterFunction` (function): Function that returns true for matching items |
| 127 | +- `callback` (function): `(error, items) => {}` |
| 128 | + |
| 129 | +```javascript |
| 130 | +// Find all adults |
| 131 | +db.findBy(item => item.age >= 18, (err, adults) => { |
| 132 | + // adults: array of matching items |
| 133 | +}); |
| 134 | + |
| 135 | +// Find by name |
| 136 | +db.findBy(item => item.name === 'John', (err, johns) => { |
| 137 | + // johns: array of items named John |
| 138 | +}); |
| 139 | +``` |
| 140 | + |
| 141 | +#### `update(id, data, callback)` |
| 142 | + |
| 143 | +Updates an existing item. |
| 144 | + |
| 145 | +- `id` (any): The ID of the item to update |
| 146 | +- `data` (object): The data to update (merged with existing item) |
| 147 | +- `callback` (function): `(error, updatedItem) => {}` |
| 148 | + |
| 149 | +```javascript |
| 150 | +db.update(1, { age: 25, city: 'New York' }, (err, updated) => { |
| 151 | + // updated: the item with merged data |
| 152 | +}); |
| 153 | +``` |
| 154 | + |
| 155 | +#### `delete(id, callback)` |
| 156 | + |
| 157 | +Deletes an item by ID. |
| 158 | + |
| 159 | +- `id` (any): The ID of the item to delete |
| 160 | +- `callback` (function): `(error, deletedItem) => {}` |
| 161 | + |
| 162 | +```javascript |
| 163 | +db.delete(1, (err, deleted) => { |
| 164 | + // deleted: the item that was removed |
| 165 | +}); |
| 166 | +``` |
| 167 | + |
| 168 | +#### `count(callback)` |
| 169 | + |
| 170 | +Returns the total number of items. |
| 171 | + |
| 172 | +- `callback` (function): `(error, count) => {}` |
| 173 | + |
| 174 | +```javascript |
| 175 | +db.count((err, total) => { |
| 176 | + console.log('Total items:', total); |
| 177 | +}); |
| 178 | +``` |
| 179 | + |
| 180 | +#### `writeAll(items, callback)` |
| 181 | + |
| 182 | +Replaces all data in the file with a new array of items. |
| 183 | + |
| 184 | +- `items` (array): Array of items to write |
| 185 | +- `callback` (function): `(error) => {}` |
| 186 | + |
| 187 | +```javascript |
| 188 | +const newData = [ |
| 189 | + { name: 'Item 1', id: 1 }, |
| 190 | + { name: 'Item 2', id: 2 } |
| 191 | +]; |
| 192 | + |
| 193 | +db.writeAll(newData, (err) => { |
| 194 | + if (!err) console.log('Data replaced successfully'); |
| 195 | +}); |
| 196 | +``` |
| 197 | + |
| 198 | +## Advanced Features |
| 199 | + |
| 200 | +### Auto-ID Assignment |
| 201 | + |
| 202 | +When creating items without an ID, JsonFileCRUD automatically assigns the next available numeric ID: |
| 203 | + |
| 204 | +```javascript |
| 205 | +db.create({ name: 'John' }, (err, result) => { |
| 206 | + // result: { name: 'John', id: 1 } |
| 207 | +}); |
| 208 | + |
| 209 | +db.create({ name: 'Jane' }, (err, result) => { |
| 210 | + // result: { name: 'Jane', id: 2 } |
| 211 | +}); |
| 212 | +``` |
| 213 | + |
| 214 | +### Concurrent Operations |
| 215 | + |
| 216 | +All write operations are automatically queued to prevent race conditions: |
| 217 | + |
| 218 | +```javascript |
| 219 | +// These will be executed sequentially, not simultaneously |
| 220 | +db.create({ name: 'User 1' }, callback1); |
| 221 | +db.create({ name: 'User 2' }, callback2); |
| 222 | +db.update(1, { active: true }, callback3); |
| 223 | +``` |
| 224 | + |
| 225 | +### Custom ID Fields |
| 226 | + |
| 227 | +You can use any field name as the primary key: |
| 228 | + |
| 229 | +```javascript |
| 230 | +const products = new JsonFileCRUD('./products.json', { idField: 'productId' }); |
| 231 | + |
| 232 | +products.create({ name: 'Laptop', price: 999 }, (err, product) => { |
| 233 | + // product: { name: 'Laptop', price: 999, productId: 1 } |
| 234 | +}); |
| 235 | +``` |
| 236 | + |
| 237 | +### Error Handling |
| 238 | + |
| 239 | +JsonFileCRUD provides detailed error messages for common scenarios: |
| 240 | + |
| 241 | +```javascript |
| 242 | +// Validation errors |
| 243 | +db.create(null, (err) => { |
| 244 | + // err.message: "Item must be an object" |
| 245 | +}); |
| 246 | + |
| 247 | +// Not found errors |
| 248 | +db.findById(999, (err) => { |
| 249 | + // err.message: "Item with id 999 not found" |
| 250 | +}); |
| 251 | + |
| 252 | +// Duplicate ID errors |
| 253 | +db.create({ id: 1, name: 'Duplicate' }, (err) => { |
| 254 | + // err.message: "Item with id 1 already exists" |
| 255 | +}); |
| 256 | +``` |
| 257 | + |
| 258 | +## Examples |
| 259 | + |
| 260 | +For comprehensive examples, see the [examples](./examples/) directory: |
| 261 | + |
| 262 | +- **[Basic Usage](./examples/basic-usage.js)** - Simple CRUD operations |
| 263 | +- **[Advanced Features](./examples/advanced-usage.js)** - Concurrent operations, filtering, custom ID fields |
| 264 | +- **[User Management](./examples/user-management.js)** - Real-world application example |
| 265 | + |
| 266 | +To run examples: |
| 267 | + |
| 268 | +```bash |
| 269 | +cd examples |
| 270 | +node basic-usage.js |
| 271 | +``` |
| 272 | + |
| 273 | +## TypeScript Support |
| 274 | + |
| 275 | +While this library is written in JavaScript, you can use it in TypeScript projects. Type definitions may be added in future versions. |
| 276 | + |
| 277 | +## File Format |
| 278 | + |
| 279 | +JsonFileCRUD stores data as a JSON array in the specified file: |
| 280 | + |
| 281 | +```json |
| 282 | +[ |
| 283 | + { "id": 1, "name": "John", "age": 30 }, |
| 284 | + { "id": 2, "name": "Jane", "age": 25 } |
| 285 | +] |
| 286 | +``` |
| 287 | + |
| 288 | +If the file doesn't exist, it will be created automatically on the first write operation. |
| 289 | + |
| 290 | +## Performance Considerations |
| 291 | + |
| 292 | +- **Small to medium datasets**: JsonFileCRUD is ideal for applications with up to ~10,000 items |
| 293 | +- **File I/O**: Every operation reads/writes the entire file, so performance scales with file size |
| 294 | +- **Memory usage**: The entire dataset is loaded into memory during operations |
| 295 | +- **Concurrent access**: Operations are queued, so high-concurrency scenarios may experience delays |
| 296 | + |
| 297 | +For larger datasets or high-performance requirements, consider using a dedicated database. |
| 298 | + |
| 299 | +## Contributing |
| 300 | + |
| 301 | +Contributions are welcome! Here are some ways you can help improve JsonFileCRUD: |
| 302 | + |
| 303 | +### Ideas for Contributions |
| 304 | + |
| 305 | +- **TypeScript Support**: Add TypeScript type definitions (.d.ts files) |
| 306 | +- **Async/Await Support**: Add Promise-based API alongside callbacks |
| 307 | +- **Batch Operations**: Add bulk insert/update/delete operations |
| 308 | +- **File Locking**: Add file locking for multi-process safety |
| 309 | +- **Documentation**: Improve documentation and add more examples |
| 310 | + |
| 311 | +### How to Contribute |
| 312 | + |
| 313 | +1. **Fork the Repository**: Create your own fork of the project |
| 314 | +2. **Create a Feature Branch**: `git checkout -b feature/your-feature-name` |
| 315 | +3. **Write Tests**: Ensure your changes are well-tested |
| 316 | +4. **Follow Code Style**: Keep the code clean and consistent |
| 317 | +5. **Update Documentation**: Add or update relevant documentation |
| 318 | +6. **Submit a Pull Request**: Describe your changes clearly |
| 319 | + |
44 | 320 | ## License
|
45 | 321 |
|
46 |
| -MIT |
| 322 | +MIT License - see [LICENSE](./LICENSE) file for details. |
| 323 | + |
0 commit comments