{"id":13808795,"url":"https://github.com/cyrilletuzi/angular-async-local-storage","last_synced_at":"2025-05-15T13:09:07.110Z","repository":{"id":12648998,"uuid":"72448978","full_name":"cyrilletuzi/angular-async-local-storage","owner":"cyrilletuzi","description":"Efficient client-side storage for Angular: simple API + performance + Observables + validation","archived":false,"fork":false,"pushed_at":"2025-03-24T18:22:25.000Z","size":1088,"stargazers_count":673,"open_issues_count":2,"forks_count":69,"subscribers_count":17,"default_branch":"main","last_synced_at":"2025-04-12T13:57:36.420Z","etag":null,"topics":["angular","indexeddb","javascript","localstorage","rxjs","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cyrilletuzi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"cyrilletuzi"}},"created_at":"2016-10-31T15:16:49.000Z","updated_at":"2025-03-24T18:22:29.000Z","dependencies_parsed_at":"2023-01-13T17:03:38.977Z","dependency_job_id":"ea6ff5d0-0827-4949-982b-566c0d9417ed","html_url":"https://github.com/cyrilletuzi/angular-async-local-storage","commit_stats":{"total_commits":15,"total_committers":1,"mean_commits":15.0,"dds":0.0,"last_synced_commit":"4a7b521d1211c0945d5d37ecb6c0140b30f90321"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyrilletuzi%2Fangular-async-local-storage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyrilletuzi%2Fangular-async-local-storage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyrilletuzi%2Fangular-async-local-storage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cyrilletuzi%2Fangular-async-local-storage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cyrilletuzi","download_url":"https://codeload.github.com/cyrilletuzi/angular-async-local-storage/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254249015,"owners_count":22039019,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","indexeddb","javascript","localstorage","rxjs","typescript"],"created_at":"2024-08-04T01:01:52.198Z","updated_at":"2025-05-15T13:09:02.102Z","avatar_url":"https://github.com/cyrilletuzi.png","language":"TypeScript","funding_links":["https://github.com/sponsors/cyrilletuzi"],"categories":["Third Party Components"],"sub_categories":["Storage"],"readme":"# Async local storage for Angular\n\nEfficient client-side storage for Angular:\n- **simplicity**: simple API similar to native `localStorage`,\n- **perfomance**: internally stored via the asynchronous `indexedDB` API,\n- **Angular-like**: wrapped in RxJS `Observable`s,\n- **security**: validate data with a JSON Schema or with [`typebox`](https://github.com/sinclairzx81/typebox),\n- **compatibility**: works around some browsers issues and heavily tested via GitHub Actions,\n- **documentation**: API fully explained, and a changelog!\n\n\u003e [!TIP]\n\u003e I am also the author of the [Angular Schematics extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=cyrilletuzi.angular-schematics), installed 1 million times. Feel free to give it a try.\n\n## Why this library?\n\nAngular does not provide a client-side storage service, and almost every app needs some client-side storage. There are 2 native JavaScript APIs available:\n- [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage)\n- [indexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)\n\nThe `localStorage` API is simple to use but synchronous, so if you use it too often, your app will soon begin to freeze.\n\nThe `indexedDB` API is asynchronous and efficient, but it is a mess to use:  you will soon be caught by the callback hell, as it does not support `Promise`s.\n\nThis library has a simple API similar to native `localStorage`, but internally stores data via the asynchronous `indexedDB` for performance. All of this powered by [RxJS](https://rxjs.dev/).\n\n## Getting started\n\nInstall the package:\n\n```bash\n# For Angular LTS (Angular \u003e= 17):\nng add @ngx-pwa/local-storage\n```\n\n*Done!*\n\nIf for any reason `ng add` does not work, follow the [manual installation guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/MANUAL_INSTALLATION.md).\n\n\u003e [!NOTE]\n\u003e Angular versions \u003c= 15 are [officially outdated](https://angular.dev/reference/versions).\n\n### Upgrading\n\nTo update to new versions, see the **[migration guides](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/MIGRATION.md).**\n\n## API\n\n```typescript\nimport { StorageMap } from '@ngx-pwa/local-storage';\n\n@Injectable()\nexport class YourService {\n  constructor(private storage: StorageMap) {}\n}\n```\n\nThis service API is similar to the standard [`Map` API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), and close to the standard [`localStorage` API](https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage).\n\n```typescript\nclass StorageMap {\n  // Write\n  set(index: string, value: unknown): Observable\u003cundefined\u003e {}\n  delete(index: string): Observable\u003cundefined\u003e {}\n  clear(): Observable\u003cundefined\u003e {}\n\n  // Read (one-time)\n  get(index: string): Observable\u003cunknown\u003e {}\n  get\u003cT\u003e(index: string, schema: JSONSchema): Observable\u003cT\u003e {}\n\n  // Advanced\n  watch(index: string): Observable\u003cunknown\u003e {}\n  watch\u003cT\u003e(index: string, schema: JSONSchema): Observable\u003cT\u003e {}\n  size: Observable\u003cnumber\u003e;\n  has(index: string): Observable\u003cboolean\u003e {}\n  keys(): Observable\u003cstring\u003e {}\n}\n```\n\n## How to\n\n### Writing data\n\n```typescript\nconst user: User = { firstName: 'Henri', lastName: 'Bergson' };\n\nthis.storage.set('user', user).subscribe(() =\u003e {});\n```\n\n\u003e [!NOTE]\n\u003e You can store any value, without worrying about serializing. But note that:\n\u003e - storing `null` or `undefined` makes no sense and can cause issues in some browsers, so the item will be removed instead,\n\u003e - you should stick to serializable JSON data, meaning primitive types, arrays and *literal* objects. `Date`, `Map`, `Set`, `Blob` and other special structures can cause issues in some scenarios. Read the [serialization guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/SERIALIZATION.md) for more details.\n\n### Deleting data\n\nTo delete one item:\n```typescript\nthis.storage.delete('user').subscribe(() =\u003e {});\n```\n\nTo delete all items:\n```typescript\nthis.storage.clear().subscribe(() =\u003e {});\n```\n\n### Reading data\n\nTo get the *current* value:\n```typescript\nthis.storage.get('user').subscribe((user) =\u003e {\n  console.log(user);\n});\n```\n\nNot finding an item is not an error, it succeeds but returns `undefined`:\n```typescript\nthis.storage.get('notexisting').subscribe((data) =\u003e {\n  data; // undefined\n});\n```\n\n\u003e [!IMPORTANT]\n\u003e You will only get *one* value: the `Observable` is here for asynchrony but is *not* meant to emit again when the stored data is changed. If you need to watch the value, read the [watching guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/WATCHING.md).\n\n### Checking data\n\nDo not forget it is client-side storage: **always check the data**, as it could have been forged.\n\nYou **should** use a [JSON Schema](http://json-schema.org/) to validate the data.\n\n```typescript\nthis.storage.get('test', { type: 'string' }).subscribe({\n  next: (user) =\u003e { /* Called if data is valid or `undefined` */ },\n  error: (error) =\u003e { /* Called if data is invalid */ },\n});\n```\n\n\u003e [!TIP]\n\u003e Read the [full validation guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/VALIDATION.md) to learn how to validate all common scenarios.\n\n### Subscription\n\nYou do *NOT* need to unsubscribe: the `Observable` autocompletes (like in the Angular `HttpClient` service).\n\nBut **you *DO* need to subscribe**, even if you do not have something specific to do after writing in storage (because it is how RxJS `Observable`s work).\n\n```typescript\nthis.storage.set('user', user); // Does nothing\n```\n\n### Errors\n\nAs usual, it is better to catch any potential error:\n```typescript\nthis.storage.set('color', 'red').subscribe({\n  next: () =\u003e {},\n  error: (error) =\u003e {},\n});\n```\n\nFor read operations, you can also manage errors by providing a default value:\n```typescript\nimport { catchError, of } from 'rxjs';\n\nthis.storage.get('color').pipe(\n  catchError(() =\u003e of('red')),\n).subscribe((result) =\u003e {});\n```\n\n\u003e [!TIP]\n\u003e Read the [errors guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/ERRORS.md) for some details about what errors can happen.\n\n### Expiration\n\nThis lib, as native `localStorage` and `indexedDb`, is about *persistent* storage.\n\nWanting *temporary* storage (like `sessionStorage`) is a very common misconception: an application does not need that.\n\n\u003e [!TIP]\n\u003e Read [the expiration guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/EXPIRATION.md).\n\n### `Map`-like operations\n\nIn addition to the classic `localStorage`-like API, this library also provides a `Map`-like API for advanced operations:\n  - `.keys()`\n  - `.has(key)`\n  - `.size`\n\n\u003e [!TIP]\n\u003e Read the [Map-like operations guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/MAP_OPERATIONS.md) for more info and some recipes. For example, it allows to implement a multiple databases scenario.\n\n## Support\n\n### Browser support\n\nThis library supports [the same browsers as Angular](https://angular.dev/reference/versions#browser-support).\n\n\u003e [!TIP]\nRead [the browsers support guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/BROWSERS_SUPPORT.md) for more details and special cases.\n\n### Collision\n\nThe library has configurable options if you have multiple apps on the same *sub*domain *and* you do not want to share data between them.\n\n\u003e [!TIP]\n\u003e Read the [collision guide](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/COLLISION.md).\n\n### Interoperability\n\nThe library has configurable options for interoperability when mixing this library with direct usage of native APIs or other libraries like `localForage` (which does not make sense in most cases).\n\n\u003e [!TIP]\n\u003e Read the [interoperability documentation](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/docs/INTEROPERABILITY.md).\n\n### Changelog\n\n[Changelog available here](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/CHANGELOG.md), and [migration guides here](https://github.com/cyrilletuzi/angular-async-local-storage/blob/main/MIGRATION.md).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyrilletuzi%2Fangular-async-local-storage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcyrilletuzi%2Fangular-async-local-storage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcyrilletuzi%2Fangular-async-local-storage/lists"}