{"id":21944221,"url":"https://github.com/michaelkrone/ngrx-normalizr","last_synced_at":"2025-07-10T01:33:42.173Z","repository":{"id":24209542,"uuid":"100873825","full_name":"michaelkrone/ngrx-normalizr","owner":"michaelkrone","description":"Managing normalized state in ngrx applications - transparently","archived":false,"fork":false,"pushed_at":"2022-12-06T20:00:43.000Z","size":830,"stargazers_count":50,"open_issues_count":22,"forks_count":17,"subscribers_count":5,"default_branch":"develop","last_synced_at":"2025-07-05T20:21:33.555Z","etag":null,"topics":["ngrx","normalizr"],"latest_commit_sha":null,"homepage":"https://michaelkrone.github.io/ngrx-normalizr/","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/michaelkrone.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-08-20T16:23:15.000Z","updated_at":"2022-07-01T19:16:25.000Z","dependencies_parsed_at":"2022-08-18T21:30:22.770Z","dependency_job_id":null,"html_url":"https://github.com/michaelkrone/ngrx-normalizr","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/michaelkrone/ngrx-normalizr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelkrone%2Fngrx-normalizr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelkrone%2Fngrx-normalizr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelkrone%2Fngrx-normalizr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelkrone%2Fngrx-normalizr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michaelkrone","download_url":"https://codeload.github.com/michaelkrone/ngrx-normalizr/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelkrone%2Fngrx-normalizr/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264512826,"owners_count":23620441,"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":["ngrx","normalizr"],"created_at":"2024-11-29T04:15:10.902Z","updated_at":"2025-07-10T01:33:42.152Z","avatar_url":"https://github.com/michaelkrone.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ngrx-normalizr\n\n[![Build Status](https://travis-ci.org/michaelkrone/ngrx-normalizr.svg?branch=master)](https://travis-ci.org/michaelkrone/ngrx-normalizr)\n![AOT compatible](https://img.shields.io/badge/aot-compatible-blue.svg)\n\n\u003e Managing [normalized state](https://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html) in [ngrx](https://github.com/ngrx/platform) applications, transparently.\n\nThis package provides a set of actions, reducers and selectors for handling normalization and denormalization of state data **transparently**.\n*ngrx-normalizr* uses [normalizr](https://github.com/paularmstrong/normalizr) for [normalizing](https://github.com/paularmstrong/normalizr/blob/master/docs/api.md#normalizedata-schema) and [denormalizing](https://github.com/paularmstrong/normalizr/blob/master/docs/api.md#denormalizeinput-schema-entities) data. All normalization and denormalization\nis defined by the use of [normalizr schemas](https://github.com/paularmstrong/normalizr/blob/master/docs/api.md#schema), since that's the way normalizr works. This enables selectors to use a transparent and powerful projection of state data.\n\n\u003e Releases will be published from the [`master`](https://github.com/michaelkrone/ngrx-normalizr/tree/master) branch. [Go there](https://github.com/michaelkrone/ngrx-normalizr/tree/master) for documentation that aligns with the npm repo version.\n\n## Installation\nTo install this package:\n```sh\nyarn add ngrx-normalizr\nnpm i ngrx-normalizr\n```\n\n### Peer dependencies\n*ngrx-normalizr* [@ngrx-store](https://github.com/ngrx/platform/blob/master/docs/store/README.md) as its peer dependencies, so you need to install them if not present already:\n\n\u003e *ngrx-normalizr* itself does not rely on any [Angular](https://angular.io) feature.\n\n```sh\nyarn add @ngrx/store\nnpm i @ngrx/store\n```\n\n## Usage\nAlso refer to the [Typedoc documentation](https://michaelkrone.github.io/ngrx-normalizr/).\nTo enable the normalizing reducer to store normalized data, you have to add it to your state. The best place for this might be the root state of your application, but feature states may use their own normalized state as well. Extend your state interface with the `NormalizedState` interface. The`ActionReducerMap` has to implement a reducer which reduces the state to a `NormalizedState`.\n\n```javascript\nimport { ActionReducerMap } from '@ngrx/store';\nimport { NormalizedState, normalized } from 'ngrx-normalizr';\n\nexport interface State extends NormalizedState {\n  /* ... other state properties */\n}\n\nexport const reducers: ActionReducerMap\u003cState\u003e = {\n\tnormalized,\n  /* ... other state reducers */\n};\n```\n\nIf there are no other state properties, it is sufficient to add the *ngrx-normalizr* reducer to your state reducers or simply pass it to `StoreModule.forRoot`.\n```javascript\nexport const reducers: ActionReducerMap\u003cNormalizedState\u003e = { normalized };\n```\n\nNow you have a `normalized` state property which will hold the normalized data. Do not worry about the weird name,\nyou will not have to deal with it.\n\n### Schemas\n[Schemas](https://github.com/paularmstrong/normalizr/blob/master/docs/api.md#schema) define the relations of your data.\nIn order to normalize and denormalize data, normalizr needs to be feed with a schema. In this example, a user might have\nan array of pets:\n```javascript\nimport { schema } from 'normalizr';\n\nexport class Pet {\n\tid: string;\n\tname: string;\n\ttype: 'cat' | 'dog';\n}\n\nexport class User {\n\tid: string;\n\tname: string;\n\tpets: Pet[];\n}\n\nexport const petSchema = new schema.Entity('pets');\nexport const userSchema = new schema.Entity('users', { pets: [petSchema] });\n```\n\n## Add, set and remove data\nActions are used to set data in - and remove data from - the normalized store.\n\n### Adding data\nTo add data and automatically normalize it, *ngrx-normalizr* provides a `AddData` action. This action takes an object with `data` and `schema` as an argument. Entities are identified by their id attribute set in the passed schema.\nExisting entities will be overwritten by updated data, new entities will be added to the store. For adding related childs, an `AddChildData` action is provided.\n\n###### Using `AddData` in an effect\n```javascript\n@Effect()\nloadEffect$ = this.actions$\n  .ofType(LOAD)\n  .switchMap(action =\u003e this.http.get('https://example.com/api/user'))\n  .mergeMap((data: User[]) =\u003e [\n    // dispatch to add data to the store\n    new AddData\u003cUser\u003e({ data, schema }),\n    // dispatch to inform feature reducer\n    new LoadSuccess(data)\n  ])\n  .catch(err =\u003e Observable.of(new LoadFail(err)));\n```\n\n#### Adding child data\nAdding a related child data to a parent entity can be done with the `AddChildData` action. Note that for this to work, the relation has to be defined in the schema. The action takes a couple of arguments which need to be given in an object:\n\n* `data`: Array of child entities to add\n* `childSchema`The `schema.Entity` of the child entity\n* `parentSchema`: The `schema.Entity` of the parent entity\n* `parentId`: The id of the entity to add child references to\n\n###### Using `AddChildData` in an effect\n```javascript\n@Effect()\naddPetEffect$ = this.actions$\n  .ofType(ADD_PET)\n  .switchMap(action =\u003e this.http.post('https://example.com/api/pets'))\n  .mergeMap((data: Pet[]) =\u003e [\n    // dispatch to add data to the store\n    new AddChildData\u003cPet\u003e({ data, childSchema, parentSchema, parentId }),\n    // dispatch to inform feature reducer\n    new AddPetSuccess(data)\n  ])\n  .catch(err =\u003e Observable.of(new LoadFail(err)));\n```\n\n### Setting data\nThe `SetData` action will overwrite all entities for a given schema with the normalized entities of the `data` property of the action constructor argument. This action can\nbe used for resetting entity state data instead of adding and updating existing entities.\n\n### Removing data\nTo remove data, *ngrx-normalizr* provides a `RemoveData` action.\nThis action takes an object with `id`, `schema` and an optional `removeChildren` property as constructor argument. The schema entity with the given id will be removed. If `removeChildren` is a map of the schema key mapped to an object property, all referenced child entities will also be removed from the store. This is handy for 1:1 relations, since only removing the parent entity may leave unused child entities in the store.\n\n###### Using `RemoveData` in an effect\n```javascript\n@Effect()\nremoveEffect$ = this.actions$\n  .ofType(REMOVE)\n  .switchMap((action: Remove) =\u003e this.http.delete(`https://example.com/api/user/${action.payload.id}`))\n  .mergeMap(result =\u003e [\n    // dispatch to remove data from the store\n    new RemoveData({ id: result.id, schema, removeChildren: { pets: 'pets' } }),\n    // dispatch to inform feature reducer\n    new RemoveSuccess()\n  ])\n  .catch(err =\u003e Observable.of(new RemoveFail(err)));\n```\n#### Removing child data\nRemoving a child entity which is 1:1 related to a parent entity can be done with the `RemoveChildData` action. Note that for this to work, the relation has to be defined in the schema. The action takes a couple of arguments which need to be given in an object:\n\n* `id`: Id of the child entity that should be removed\n* `childSchema`The `schema.Entity` of the child entity\n* `parentSchema`: The `schema.Entity` of the parent entity\n* `parentId`: The id of the entity to remove child references from\n\n###### Using `AddChildData` in an effect\n```javascript\n@Effect()\nremovePetEffect$ = this.actions$\n  .ofType(REMOVE_PET)\n  .switchMap(action =\u003e this.http.remove(`https://example.com/api/pets/${action.payload.id}`))\n  .mergeMap((data: Pet) =\u003e [\n    // dispatch to add data to the store\n    new RemoveChildData({ id: data.id, childSchema, parentSchema, parentId }),\n    // dispatch to inform feature reducer\n    new RemovePetSuccess(data)\n  ])\n  .catch(err =\u003e Observable.of(new LoadFail(err)));\n```\n\n### Action creators\nFor convenience, *ngrx-normalizr* provides an `actionCreators` function which will return an object with following schema bound action creators:\n* `setData` - `(data: T[]) =\u003e SetData\u003cT\u003e`\n* `addData` - `(data: T[]) =\u003e AddData\u003cT\u003e`\n* `addChildData\u003cC\u003e` - `(data: C[], childSchema: schema.Entity, parentId: string) =\u003e AddChildData`\n* `removeData` - `(id: string, removeChildren?: SchemaMap) =\u003e RemoveData`\n* `removeChildData` - `(id: string, childSchema: schema.Entity, parentId: string) =\u003e RemoveChildData`\n\nAction creators could be exported along whith other feature actions:\n```javascript\nimport { actionCreators } from 'ngrx-normalizr';\n\nconst creators = actionCreators\u003cUser\u003e(userSchema);\nexport const setUserData = creators.setData;\nexport const addUserData = creators.addData;\nexport const removeUserData = creators.removeData;\n```\n\nUsing the action creator in an Effect class:\n\n###### Using the `removeUserData` action creator in an effect\n```javascript\nimport { removeUserData } from '../actions';\n\n@Effect()\nremoveEffect$ = this.actions$\n  .ofType(REMOVE)\n  .switchMap((action: Remove) =\u003e this.http.delete(`https://example.com/api/user/${action.payload.id}`))\n  .mergeMap(result =\u003e [\n    // dispatch to remove data from the store\n    removeUserData(id: result.id, { pets: 'pets' }),\n    // dispatch to inform feature reducer\n    new RemoveSuccess()\n  ])\n  .catch(err =\u003e Observable.of(new RemoveFail(err)));\n```\n\n## Query state data\n*ngrx-normalizr* provides two simple selectors and two simple projector functions to query the state and project/denormalize the result.\n\n### Creating Schema selectors\nTo transparently query data from the store from a feature module, selectors are provided by the `createSchemaSelectors` function.\nIt takes an entity schema to create schema bound selectors:\n\n```javascript\nimport { createSchemaSelectors } from 'ngrx-normalizr';\nimport { User } from '../classes/user';\n\nconst schemaSelectors = createSchemaSelectors\u003cUser\u003e(userSchema);\n```\n\n`createSchemaSelectors` will return schema bound selectors (instance of `SchemaSelectors`):\n* `getEntities` - ` MemoizedSelector\u003c{}, T[]\u003e` Returns all denormalized entities for the schema\n* `getNormalizedEntities` - `MemoizedSelector\u003cany, EntityMap\u003e` Returns all normalized (raw) state entities of every schema (the whole entities state)\n* `entitiesProjector` - `(entities: {}, ids?: Array\u003cstring\u003e) =\u003e T[]` Projector function for denormalizing a the set of normalized entities to an denormalized entity array. If no `ids` are given, all entities will be denormalized.\n* `entityProjector` - `(entities: {}, id: string) =\u003e T` Projector function for denormalizing a single normalized entity with the given id\n\nYou might create several selectors with several schemas, i.e. a *listView* schema, which only denormalizes the data used in the list\nview, and a *detailView* schema, to completely denormalize a given entity.\n\n### Using schema selectors\nFeature selectors can use the schema bound selectors and projector functions to query entity data from the store. To get all denormalized\nentities, you might simply use the `getEntities` selector like this:\n\n```javascript\n// store.select(getUsers) will give all denormalized user entities\nconst getUsers = schemaSelectors.getEntities;\n```\nUnder the hood this does something similar to this:\n```javascript\n// equivalent alternative\nconst getUsers = createSelector(\n  schemaSelectors.getNormalizedEntities,\n  schemaSelectors.entitiesProjector\n);\n\n```\nThe `entitiesProjector` simply takes an object of normalized entity data and applies the denormalization with the bound schema. Optionally an array of id strings can be passed as a second parameter to perform denormalization for the given id's only.\n\n#### Composing schema selectors\nTo query and denormalize specific data you can use the *@ngrx/store* [`createSelectors`](https://github.com/ngrx/platform/blob/master/docs/store/selectors.md#createselector) function and compose them with the schema bound\nselectors:\n\n```javascript\nimport { createSelector } from '@ngrx/store';\n\nexport const getSelectedUserId = createSelector(\n  userFeatureSelector,\n  user.getSelectedId\n);\n\n// store.select(getSelectedUser) will give the denormalized selected user\nconst getSelectedUser = createSelector(\n  schemaSelectors.getNormalizedEntities,\n  getSelectedUserId,\n  schemaSelectors.entityProjector\n);\n```\n`entityProjector` will simply take an object of denormalized entities and apply the denormalization with the bound schema only for the given id. Note that you might also select data from the denormalized result and providing your own selector:\n```javascript\nconst getSelectedUserWithPetsOnly = createSelector(\n  getUsers,\n  getSelectedId,\n  (entities, id) =\u003e entities.find(e =\u003e e.id === id \u0026\u0026 e.pets.length \u003e 0)\n);\n```\n\n## Meta\n\nMichael Krone – [@DevDig](https://twitter.com/DevDig) – michael.krone@outlook.com and all [CONTRIBUTORS](https://github.com/michaelkrone/ngrx-normalizr/graphs/contributors)\n\nDistributed under the MIT license. See [``LICENSE``](https://github.com/michaelkrone/ngrx-normalizr/blob/master/LICENSE) for more information.\n\n[https://github.com/michaelkrone/ngrx-normalizr](https://github.com/michaelkrone/ngrx-normalizr)\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/michaelkrone/ngrx-normalizr\u003e)\n2. Create your feature branch (`git checkout -b feature/fooBar`)\n3. Commit your changes (`git commit -am 'Add some fooBar'`)\n4. Push to the branch (`git push origin feature/fooBar`)\n5. Create a new [Pull Request](https://github.com/michaelkrone/ngrx-normalizr/compare?expand=1)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelkrone%2Fngrx-normalizr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichaelkrone%2Fngrx-normalizr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelkrone%2Fngrx-normalizr/lists"}