{"id":16742066,"url":"https://github.com/dkzlv/micro-transform","last_synced_at":"2025-07-17T05:35:08.211Z","repository":{"id":149268698,"uuid":"596660460","full_name":"dkzlv/micro-transform","owner":"dkzlv","description":"Super simple Typescript serializer","archived":false,"fork":false,"pushed_at":"2023-09-08T09:31:19.000Z","size":86,"stargazers_count":45,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-01T03:28:43.050Z","etag":null,"topics":["deserializer","mapper","serializer","transformer","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/dkzlv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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}},"created_at":"2023-02-02T17:04:24.000Z","updated_at":"2024-10-19T18:17:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"5b5bb492-02da-44d7-ac5d-d95945444082","html_url":"https://github.com/dkzlv/micro-transform","commit_stats":{"total_commits":25,"total_committers":1,"mean_commits":25.0,"dds":0.0,"last_synced_commit":"2efbcc89b28ad039d4b40bb30a535d338341a02d"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dkzlv%2Fmicro-transform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dkzlv%2Fmicro-transform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dkzlv%2Fmicro-transform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dkzlv%2Fmicro-transform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dkzlv","download_url":"https://codeload.github.com/dkzlv/micro-transform/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227802822,"owners_count":17822110,"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":["deserializer","mapper","serializer","transformer","typescript"],"created_at":"2024-10-13T01:08:43.519Z","updated_at":"2024-12-03T20:39:23.218Z","avatar_url":"https://github.com/dkzlv.png","language":"TypeScript","readme":"# Micro Transform\n\nTiny library to transform objects between different states.\n\nSuitable cases: serializing, deserializing, transforming into different shapes.\n\n- **Micro in size**. No deps. ~500B.\n- **TS-first**. All results of transformations are strictly typed and easily\ninspectable on hover.\n- **Versatile**. Uses plain functions under the hood, can be composed in any way.\n\n\u003ca href=\"https://evilmartians.com/?utm_source=micro-transform\"\u003e\n  \u003cimg src=\"https://evilmartians.com/badges/sponsored-by-evil-martians.svg\"\n       alt=\"Sponsored by Evil Martians\" width=\"236\" height=\"54\"\u003e\n\u003c/a\u003e\n\n## Install\n\nTry it online [on StackBlitz](https://stackblitz.com/edit/react-ts-frmvd6?file=App.tsx).\n\nOr install using your favorite package manager:\n\n`npm install micro-transform`\n\n# Usage\n\n## `.setModelConfig`\n\nYou can use this method to cherry-pick fields from a passed model. Possible values:\n\n1. `true` — this will include the field as is, without any transformations.\n2. `false` — this will exclude the field, that could be previously added by\n[asterisk operator](#asterisk-operator) or previously in the [chain](#immutability-and-chaining).\n3. function — we will execute it and pass both the model and the [context](#context) \ninto this function. If it'll return `Promise`, we'll await for it (those are executed\nin parallel).\n1. nested transformers! Read more about it in the [Nested Transformers](#nested-transformers)\nsection.\n\n```ts\nimport { createTransformer } from \"micro-transform\";\n\nconst userExample = {\n  id: \"id\",\n  createdAt: 1602201600000,\n  email: \"olivia@EXAMPLE.COM\",\n  password: \"secret\",\n};\ntype User = typeof userExample;\n\nconst userSerializer = createTransformer\u003cUser\u003e().setModelConfig({\n  id: true,\n  email: (user) =\u003e user.email.toLowerCase(),\n});\n\nconst result = await userSerializer.transform(userExample);\n// -\u003e { id: \"id\", email: \"olivia@example.com\" }\n```\n\n### Asterisk operator\n\nThere's a special case for the `setModelConfig`: you can pass a key `\"*\"` to include\nall fields instead of listing them one by one.\n\n```ts\nconst userSerializer = createTransformer\u003cUser\u003e().setModelConfig({\n  \"*\": true,\n  password: false,\n});\nconst result = await userSerializer.transform(userExample);\n// -\u003e { id: \"id\", createdAt: 1602201600000, email: \"olivia@EXAMPLE.COM\" }\n```\n\n### Infer the result type\n\nThe library ships with a small importable helper that can help you work with the\nresulting data on the type level:\n\n```ts\nimport type { TransformerResult } from 'micro-transform';\n\nconst dateSerializer = createTransformer\u003cUser\u003e().setModelConfig({\n  createdAt: (user) =\u003e formatDate(user.createdAt),\n});\n\ntype SerializedDate = TransformerResult\u003ctypeof dateSerializer\u003e;\n// { createdAt: string }\n```\n\n## `.setCustomConfig`\n\nWith this you can enchance models with **new** fields. The key would be the new field\nname. As of values, it pretty much repeats the model config:\n\n1. function — that is the primary case, basically a computed property.\n2. `false` — that will exclude the custom field previously added in the chain.\n\n```ts\nconst userRoleSerializer = createTransformer\u003cUser\u003e().setCustomConfig({\n  role: async (user) =\u003e {\n    return db.fetchRole(user.id);\n  },\n});\n\nconst result = await userRoleSerializer.transform(userExample);\n// -\u003e { role: \"admin\" }\n```\n\n## Context\n\nSometimes you need a bit of extra context to transform data. Say, you need user's\nlocale to pick the correct translation, or user's timezone to localize time fields.\n\nIn this case you can define context's structure its structure as the second type\nargument. You'll then need to pass it to `transform` function. If you do this right,\nyou will be able to read the value in field-level transformer functions:\n\n```ts\nconst userCreationSerializer = createTransformer\u003c\n  User,\n  { timezone: string }\n\u003e().setModelConfig({\n  createdAt: (user, ctx) =\u003e formatDate(user.createdAt, ctx.timezone),\n});\n\nconst result = await userCreationSerializer.transform(userExample, {\n  timezone: \"Europe/Lisbon\",\n}); // -\u003e { createdAt: \"...\" }\n```\n\n## Nested Transformers\n\nSince we accept functions on the field-level transformers, you *could have* made your\nown solution for nested transformers, but instead we added a built-in solution for that!\n\nYou can pass a transformer as field value, and it will:\n\n1. correctly transform both arrays and single entities\n2. pass on the context from the root transformer to nested transformers\n3. evaluate all promises in parallel\n\n```ts\ntype UserWithFriends = User \u0026 { friends: User[], bestFriend: User };\n\ndeclare const user: UserWithFriends;\n\nconst friendSerializer = createTransformer\u003cUser\u003e().setModelConfig({\n  email: true,\n});\nconst userSerializer = createTransformer\u003cUserWithFriends\u003e().setModelConfig({\n  id: true,\n  friends: friendSerializer,\n  bestFriend: friendSerializer,\n});\n\nconst serializedUser = await userSerializer.transform(user);\n// { id: string, friends: { email: string }[], bestFriend: { email: string } }\n```\n\n## Immutability and Chaining\n\nAs you might have noticed, the basic API involves chaining model configs and\ncustom configs. And, as you might have guessed by the header, all the configs\nare merged (not replaced), and all the intermediate transformers are immutable.\n\nYou can you this to generate configs that are overall very similar, but differ in\nsmall details. For example, if you add or remove fields between different API versions,\nor user groups (admin, public, etc.).\n\n```ts\nconst adminUserSerializer = createTransformer\u003cUser\u003e()\n  .setModelConfig({\n    \"*\": true,\n  })\n  .setCustomConfig({ role: (user) =\u003e db.fetchRole(user.id) });\n\n// Hiding password\nconst moderatorUserSerializer = adminUserSerializer.setModelConfig({\n  password: false,\n});\n\n// Hiding role\nconst publicUserSerializer = moderatorUserSerializer.setCustomConfig({\n  role: false,\n});\n```\n\n## Validations\n\nValidations are **out of the scope** for this library. There's no validation of\nthe shape of the incoming data. If you pass in garbage, the library will crash,\nand it's intended.\n\nIf you have untrusted/unexpected input, use any of the schema validation libraries\nout there, like [zod](https://github.com/colinhacks/zod),\n[yup](https://github.com/jquense/yup), [valita](https://github.com/badrap/valita)\nand numerous others.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdkzlv%2Fmicro-transform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdkzlv%2Fmicro-transform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdkzlv%2Fmicro-transform/lists"}