How to rehydrate typed data?
Tags: typescript, javascript
Published: 2022-05-26 12:02:33

## The problem

I have a simple blog with JSON file used as database. I would like to get all posts and display them in a list.

Below you can see my `Post` interface:

```ts
interface Post {
  authorId: number;
  title: string;
  tags: string[];
  content: string;
  state: 'draft' | 'published';
  createdAt: Date;
  modifiedAt: Date;
  publishedAt: Date | null;
}
```

And this is database representation in JSON:

```json
{
  "posts": [{
    "authorId": 7,
    "title": "My first post",
    "tags": [],
    "content": "Hello world!",
    "state": "draft",
    "createdAt": "2022-05-20T17:21:34.000Z",
    "modifiedAt": "2022-05-23T18:45:17.000Z",
    "publishedAt": null
  }]
}
```

When I parse this with `JSON.parse`, I get type mismatch on `createdAt`, `modifiedAt` and `publishedAt` since they are all strings instead of `Date` objects.

The question is: how can I fix this mismatch?

---

## The Solution

The first idea that comes to mind is to `map` over the list and manually convert to proper objects

```ts
const parsedData = JSON.parse(jsonData);

const data = {
  posts: parsedData.posts.map((post) => {
    return {
      ...post,
      createdAt: new Date(post.createdAt),
      modifiedAt: new Date(post.modifiedAt),
      publishedAt: post.publishedAt ? new Date(post.publishedAt) : null
    };
  })
};
```

This solution works, but has one issue: I need to repeat this conversion every time I want to get `Post` objects. Additionally, after making changes to `Post` interface, I need to reflect them in all places.

Let's improve this a little.

### Creating a function

I am going to extract conversion code to functions for reducing repetition. It will also make maintenance easier.

```ts
function parsePost(post) {
  return {
    ...post,
    createdAt: new Date(post.createdAt),
    modifiedAt: new Date(post.modifiedAt),
    publishedAt: post.publishedAt ? new Date(post.publishedAt) : null
  };
}

function parsePosts(list) {
  return list.map(parsePost);
}
```

I can freely use this function as needed:

```ts
const parsedData = JSON.parse(jsonData);
const data = {
  posts: parsePosts(parsedData.posts)
};
```

### Using a library

Although the current solution works, is simple and reusable, I can go one step further.

Some time ago, I found an interesting [library](https://www.npmjs.com/package/hydration), that allows to serialize JS objects and save information about its type. It was a few years old, so I decided to create a modern version of it.

Let me present to you: [hydration-next](https://www.npmjs.com/package/hydration-next).

---

At the beginning, I need to add type info to my post. For this, I can use `dehydrate` function. It returns an object with additional `_types` field. There is also a `stringify` function, which is a shortcut for `JSON.stringify(dehydrate(data))`. 

Here you can see how my JSON looks now:

```json
{
  "authorId": "7",
  "title": "My first post",
  "tags": {},
  "content": "Hello world!",
  "state": "draft",
  "createdAt": 1653067294000,
  "modifiedAt": 1653331517000,
  "publishedAt": "",
  "_types": {
    "authorId": "number",
    "title": "string",
    "tags": "array",
    "content": "string",
    "state": "string",
    "createdAt": "Date",
    "modifiedAt": "Date",
    "publishedAt": "null"
  }
}
```

I can safely store it in file, local storage or elsewhere.

For the other way, I can use `hydrate` function. Similarly, there is a `parse` function, which is a shortcut for `hydrate(JSON.parse(data))`.

Primitive data types (string, boolean, number, null) are supported out-of-the-box, as well as arrays, objects, `Date`, `RegExp` and `Buffer`. You are also able to add custom types and define functions to hydrate and dehydrate them.

Here you can see a previous example, this time with `hydration-next`:

```ts
import { parse } from 'hydration-next';

// ...

const data = parse(jsonData);
```

---

## The End

I hope you enjoyed this post. If you decide to use my package and find any issues with it, please [let me know](https://github.com/kamil7x/hydration-next/issues). I would like to make it as good as possible.

See you next time!