@teslanick/normalizr
v4.1.0
Published
Normalizes and denormalizes JSON according to schema for Redux and Flux applications
Downloads
271
Maintainers
Readme
normalizr
Normalizes and denormalizes JSON according to schema for Redux and Flux applications.
Install
npm install normalizrMotivation
Many APIs, public or not, return JSON data that has deeply nested objects. Using data in this kind of structure is often very difficult for JavaScript applications, especially those using Flux or Redux.
Solution
Normalizr is a small, but powerful utility for taking JSON with a schema definition and returning nested entities with their IDs, gathered in dictionaries.
Quick Start
Consider a typical blog post. The API response for a single post might look something like this:
{
"id": "123",
"author": {
"id": "1",
"name": "Paul"
},
"title": "My awesome blog post",
"comments": [
{
"id": "324",
"commenter": {
"id": "2",
"name": "Nicole"
}
}
]
}We have two nested entity types within our article: users and comments. Using various schema, we can normalize all three entity types down:
import { normalize, schema } from 'normalizr';
// Define a users schema
const user = new schema.Entity('users');
// Define your comments schema
const comment = new schema.Entity('comments', {
commenter: user,
});
// Define your article
const article = new schema.Entity('articles', {
author: user,
comments: [comment],
});
const normalizedData = normalize(originalData, article);Now, normalizedData will be:
{
result: "123",
entities: {
"articles": {
"123": {
id: "123",
author: "1",
title: "My awesome blog post",
comments: [ "324" ]
}
},
"users": {
"1": { "id": "1", "name": "Paul" },
"2": { "id": "2", "name": "Nicole" }
},
"comments": {
"324": { id: "324", "commenter": "2" }
}
}
}Denormalization
To convert normalized data back to its nested form, use denormalize:
import { denormalize } from 'normalizr';
const denormalizedArticle = denormalize('123', article, normalizedData.entities);
// Returns the original nested structureCircular References
Normalizr handles circular references in both schemas and data. When denormalizing, circular references maintain referential equality—the same object instance is returned for the same entity.
import { normalize, denormalize, schema } from 'normalizr';
const user = new schema.Entity('users');
user.define({ friends: [user] });
// Circular data: user references themselves
const input = { id: '1', name: 'Alice', friends: [] };
input.friends.push(input);
const { result, entities } = normalize(input, user);
// entities.users['1'] = { id: '1', name: 'Alice', friends: ['1'] }
const output = denormalize(result, user, entities);
// Referential equality is preserved:
output === output.friends[0]; // trueTypeScript
Normalizr is written in TypeScript and includes comprehensive type definitions. Type utilities like Denormalized<S>, Normalized<S>, and AllEntitiesOf<S> help you infer types from your schemas.
The .as<T>() Method (Recommended)
Use .as<T>() to associate a TypeScript interface with your schema while preserving full type inference for nested schemas:
import { normalize, schema, Denormalized, AllEntitiesOf } from 'normalizr';
interface User {
id: string;
name: string;
}
interface Article {
id: string;
title: string;
author: User;
}
// Create schemas with .as<T>() for full type inference
const userSchema = new schema.Entity('users').as<User>();
const articleSchema = new schema.Entity('articles', {
author: userSchema,
}).as<Article>();
// Type utilities work with full nested type information
type Entities = AllEntitiesOf<typeof articleSchema>;
// { users: Record<string, User>; articles: Record<string, Article> }Why
.as<T>()? TypeScript has a limitation with partial type parameter inference. When you writenew schema.Entity<'users', User>('users', { ... }), TypeScript uses the default{}for the unspecifiedTDefinitionparameter instead of inferring it from the constructor argument. This breaks type inference for nested schemas. The.as<T>()method sidesteps this by letting TypeScript infer all parameters first, then narrowing the data type.
Build Files
Normalizr ships with two module formats:
- ESM (
dist/normalizr.js) - ES Modules format, suitable for modern bundlers like Vite, webpack, Rollup, or esbuild. This is the default when usingimport. - CommonJS (
dist/normalizr.cjs) - CommonJS format for Node.js and older bundlers. This is the default when usingrequire().
TypeScript declaration files (dist/index.d.ts) are included for full type support.
Documentation
Credits
Normalizr was originally created by Dan Abramov and inspired by a conversation with Jing Chen. Version 3 was a complete rewrite by Paul Armstrong. Version 4 is a TypeScript rewrite by Nicholas Husher.
License
MIT
