{"id":15566876,"url":"https://github.com/ftzi/firebase-database-modeler","last_synced_at":"2025-04-23T23:47:45.615Z","repository":{"id":49596000,"uuid":"280497700","full_name":"ftzi/firebase-database-modeler","owner":"ftzi","description":"Take your Firebase Realtime Database to the next level with models!","archived":false,"fork":false,"pushed_at":"2022-11-11T01:53:39.000Z","size":110,"stargazers_count":4,"open_issues_count":4,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-11T20:08:41.777Z","etag":null,"topics":["cloud-functions","firebase","firebase-database-modeler","npm","realtime-database","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ftzi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-17T18:27:46.000Z","updated_at":"2022-10-12T00:08:43.000Z","dependencies_parsed_at":"2022-08-23T16:20:18.680Z","dependency_job_id":null,"html_url":"https://github.com/ftzi/firebase-database-modeler","commit_stats":null,"previous_names":["hfantauzzi/firebase-database-modeler","srbrahma/firebase-database-modeler","brfantauzzi/firebase-database-modeler","ftzi/firebase-database-modeler"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffirebase-database-modeler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffirebase-database-modeler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffirebase-database-modeler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ftzi%2Ffirebase-database-modeler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ftzi","download_url":"https://codeload.github.com/ftzi/firebase-database-modeler/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250295702,"owners_count":21407034,"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":["cloud-functions","firebase","firebase-database-modeler","npm","realtime-database","typescript"],"created_at":"2024-10-02T17:08:01.713Z","updated_at":"2025-04-23T23:47:45.592Z","avatar_url":"https://github.com/ftzi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Firebase Database Modeler\n\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n[![TypeScript](https://badgen.net/npm/types/env-var)](http://www.typescriptlang.org/)\n[![npm](https://img.shields.io/npm/v/firebase-database-modeler)](https://www.npmjs.com/package/firebase-database-modeler)\n[![npm](https://img.shields.io/npm/dt/firebase-database-modeler)](https://www.npmjs.com/package/firebase-database-modeler)\n\n\n\u003c/div\u003e\n\nFirebase Database Modeler upgrades your Realtime Database to a whole new level!\n\nFull and awesome Typescript support!\n\n\nSupports firebase, firebase-admin and react-native-firebase packages.\n\nREADME still being improved. Not a focus right now, as I am using this package in a full time real project development.\n\n\n# Instalation\n\n```js\nnpm install --save firebase-database-modeler\n// or\nyarn add firebase-database-modeler\n```\n\n# Usage\n\n```typescript\nimport { _, _$, _root, modelerSetDefaultDatabase } from 'firebase-database-modeler';\n\n// There are multiple ways of setting up the database depending of the firebase package\n// you are using (firebase, firebase-admin or react-native-firebase).\n// Read their docs to see how to get the firebase.database().\nconst database = firebase.database()\n\nmodelerSetDefaultDatabase(database);\n\nconst stores = _('stores', {\n  $storeId: _$({\n    name: _\u003cstring\u003e('n'), // The DB property key can be different from the model property key!\n    rating: _\u003cnumber\u003e('rating'),\n    open: _\u003cboolean\u003e('open'),\n    optionalProp: _\u003cnumber | null\u003e('oP'), // You can tag the model property as optional by adding `| null`.\n    users: _('users', {\n      $userId: _$({\n        name: _\u003cstring\u003e('name')\n      })\n    })\n  })\n})\n\nconst root = _root({\n  stores\n})\n\nasync function createStore(storeId: string, userId: string, userName: string) {\n  // Typescript IntelliSense will fully guide you to build the object!\n  // In _set(), all the model properties are required.\n  await stores.$storeId._set({\n    name: 'Cool Store', // In the model declaration, we've set this name property\n                        // to have the key 'n' in the DB. _set() automatically converts this!\n    rating: 4.2,\n    open: true,\n    users: {\n      [userId]: {\n        name: userName\n      };\n    }\n  }, storeId) // This storeId variable will be used as the $storeId path segment\n}\n\nasync function setStoreName(storeId: string, newName: string) {\n  // Typescript will complain if the _set() first argument is not a string, in this case.\n  await stores.$storeId._set(newName, storeId)\n}\n\n// The type of this function will be the store model type! This package\n// automatically converts the model schema to the DB schema!\nasync function getStore(storeId: string) {\n  return await stores.$storeId._onceVal('value', storeId)\n}\n\n```\n\n\n# API\n\n\u003cbr/\u003e\n\n## Functions\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e modelerSetDefaultDatabase (database: Database) =\u003e void\u003c/h3\u003e\u003c/b\u003e\n\nSets the default database that will be used by all Realtime Database operations you may call using your Model.\n\nYou do not need to use this if you are passing the database to the `_root()` or `._ref()` based functions.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\_ (key: string, children?: Node) =\u003e Node \u003c/h3\u003e\u003c/b\u003e\n\nCreates a Node. First parameter is the Node key: the name of it in the database.\n\nThe second parameter allows Node nesting.\n\nYou may pass a type to it.\n\n```typescript\nconst root = _('/', {\n  first: _('1st'),\n  second: _('second', {\n    nested: _\u003cstring\u003e('stuff'),\n  }),\n});\n\ndatabase.second.nested._key(); // = 'stuff'\n```\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\_\\$ (key: string, children?: Node) =\u003e Node \u003c/h3\u003e\u003c/b\u003e\n\nCreates a Variable Node. It's the same as calling `_('$', children)`.\n\n```typescript\nconst users = _('users', {\n  $userId: _$({\n    name: _\u003cstring\u003e('name'),\n    age: _\u003cnumber\u003e('age'),\n  })\n})\n```\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\_root (key: string, children?: Node, database?: Database, blockDatabase: boolean = false) =\u003e Node \u003c/h3\u003e\u003c/b\u003e\n\nCreates a Root Node. You MUST call this to your Model root to make everything work.\n\nIf you use the `database` parameter, it will apply it recursively to all Model Nodes (to the ._database property), having preference over the database that can be set with the `modelerSetDefaultDatabase()` but can be overriden by the `database` parameter in `._ref()` based functions.\n\nIf `blockDatabase == true`, an Error will be throw if used the `database` parameter in `._ref()` based functions. This is useful and safe if using more than one Model and one of them uses the `database` parameter in `._ref` and the other doesn't, so, you won't mess your DB by a mistake.\n\n```typescript\nconst root = _root({\n  users:\n  name: _\u003cstring\u003e('name'),\n  age: _\u003cnumber\u003e('age'),\n});\n```\n\n\n\u003cb\u003e\u003ch3\u003e pathSegmentIsValid (segment: string) =\u003e boolean \u003c/h3\u003e\u003c/b\u003e\n\nA path segment is 'each/part/of/a/path', separated by '/'. This function checks if the given segment is a string, and if matches the RegEx `/^[a-zA-Z0-9_-]+$/` . Useful to check if the client/server is using a valid and safe path.\n\nThis is automatically called by `_pathWithVars` (see below)\n\n\n\u003cbr/\u003e\n\n## Node properties\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._key : string \u003c/h3\u003e\u003c/b\u003e\n\nReturns the value you entered as the first argument of the \\_ function.\nIs the last part of the path.\n\n```typescript\n// E.g.:\nusers.$userId.stores._key; // Returns 'stores'\n```\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._path : string \u003c/h3\u003e\u003c/b\u003e\n\nReturns the entire path (hierarchical concatenation of all keys / segments). '\\$' keys variables aren't converted.\n\nThis property is recursively set when you call the `_root({yourModel})`, and that's why we have to call it.\n\n```typescript\n// E.g.:\nstores.$storeId.users.$userId.name._path; // Returns 'stores/$/users/$/name'\n```\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._pathWithVars (vars?: string | string[]) =\u003e string \u003c/h3\u003e\u003c/b\u003e\n\nReturns the path with **'\\$'** variables converted. For each Variable Node, you must pass\nits string value as parameter. Each `vars` item is tested with the `pathSegmentIsValid` function.\n\nIf you passed a number of variables lesser than the required or any of them have an invalid value (not a string or the string doesn't match the Regex `/^[a-zA-Z0-9_-]+$/`), an error with useful information will be throw. This will also happen in any other function here that calls uses this one.\n\n```typescript\n// E.g.:\nstores.$storeId.users.$userId_pathWithVars(['abc', '0xDEADBEEF']); // Returns 'stores/abc/users/0xDEADBEEF\n```\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._pathTo (targetNode: Node, vars?: string | string[]) =\u003e string \u003c/h3\u003e\u003c/b\u003e\n\nReturns the path from the current node to the given target node. If the target node is not a child of any level of the current node, an error is thrown. _pathWithVars(...vars) is executed. The current node key / segment isn't included in the result, but is the target node.\n\nThe `vars` here is relative: You must only pass the vars that are after the model you called the _pathTo. Example below.\n\nThis method is very useful in a update() function as the object dynamic key. Example below.\n\n\n```typescript\n// E.g.:\n\nconst m$storeId = stores.$storeId; // Just to reduce code size. This 'm' in the start of the const stands for model. I use this \"standard\" in my codes.\n\nm$storeId._pathTo(m$storeId.users.$userId); // Returns 'users/$'\n\nm$storeId._pathTo(m$storeId.users.$userId, 'xyz'); // Returns 'users/xyz'. Notice that the vars 'xyz' is for the $userId and not for the $storeId.\n\n// Example to show its functionality in update(). This example will change at the same time both users names. We do not use _update() as we are not following the object model properties directly.\nm$storeId._ref('store1').update({\n  [m$storeId._pathTo(m$storeId.users.$userId.name, 'user1')]: 'John', // _pathTo result is 'users/user1/name'\n  [m$storeId._pathTo(m$storeId.users.$userId.name, 'user2')]: 'Anna', // _pathTo result is 'users/user2/name'\n})\n```\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._dbType : ModelLikeDbData \u003c/h3\u003e\u003c/b\u003e\n\nUse it with Typescript `typeof` to get the ModelLikeDbData type of the node. Its real value is undefined, so, only useful for getting the type.\n\n**ModelLikeDbData** is a type that is almost like to the DB schema, but with the property keys still being the model ones. `~'$variableNodes: (childrenNodesType)'` types are converted to `~'[x: string]: (childrenNodesType)'`. You will read this type name a few times in this README.\n\nYou probably won't use this property directly.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._ref (vars?: string | string[], database?: Database) =\u003e Reference \u003c/h3\u003e\u003c/b\u003e\n\nReturns a Realtime Database reference while using the same working of \\_pathWithVars.\n\nYou may pass a database as argument. It has preference over the database set by `modelerSetDefaultDatabase()` or by the `_root()` or `._clone()` database argument. (Read in `_root` about `blockDatabase`.)\n\nIt is called by all the DB operations methods that will soon appear below.\n\nThe `vars` and `database` parameters will appear in another functions, with the same functionality.\n\n\n```typescript\n// E.g.:\nstores.$storeId.rating._ref('abc').set(2.7);\n```\n\n\n\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._dataToDb (data: ModelLikeDbData) =\u003e any \u003c/h3\u003e\u003c/b\u003e\n\nConverts the inputted data to your Realtime Database schema, the exact way that will appear in your DB.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._dataFromDb (data: any) =\u003e ModelLikeDbData \u003c/h3\u003e\u003c/b\u003e\n\nConverts data from the DB (received with ref.on() or ref.once()) to a Model-Like object, with typings.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._onceVal (event: EventType, vars?: string | string[], database?: Database) =\u003e ModelLikeDbData \u003c/h3\u003e\u003c/b\u003e\n\nA simple way to retrieve data from the DB once.\n\nSame as `model._dataFromDb(await model.\\_ref(vars).once(event)).val()`.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._onVal (event: EventType, callback: (data: ModelLikeDbData) =\u003e void, vars?: string | string[], database?: Database) =\u003e Reference \u003c/h3\u003e\u003c/b\u003e\n\nLike Firebase `ref.on()`, it will execute the callback for every time the event happens. This one will also execute `model._dataFromDb(snapshot.val())` in the snapshot.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._exists (vars?: string | string[], database?: Database) =\u003e Promise\\\u003cboolean\u003e \u003c/h3\u003e\u003c/b\u003e\n\nReturns if the reference exists.\nSame as `(await model._ref(vars).once('value')).exists()`\n\n```typescript\n// E.g.:\nawait stores.$storeId.rating._exists(); // Will return true or false.\n```\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._set (value: ModelLikeDbData, vars?: string | string[], database?: Database) =\u003e Promise\\\u003cany\u003e \u003c/h3\u003e\u003c/b\u003e\n\nSame as `model._ref(vars).set(model._dataToDb(value))`, with type checking on value.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._update (value: Partial\\\u003cModelLikeDbData\u003e, vars?: string | string[], database?: Database) =\u003e Promise\\\u003cany\u003e \u003c/h3\u003e\u003c/b\u003e\n\nSame as `model._ref(vars).update(model._dataToDb(value))`, with type checking on value.\n\nThe value or its children are all optional/undefined, as `update` in RTDB only changes the defined properties and keeps the current value of the undefined ones.\n\nAlso, you may now (2.8.0) pass `null` to optional properties to remove the current value.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._push (value: ModelLikeDbData, vars?: string | string[], database?: Database) =\u003e Promise\\\u003cany\u003e \u003c/h3\u003e\u003c/b\u003e\n\nSame as `model._ref(vars).push((model._dataToDb(value)))`, with type checking on value.\n\nWith the same working of ref().push(), you may pass undefined as the `value` to just create the reference (to access the client side `key` property), without actually storing the new data. To learn more about it, Google about push() with or without arguments!\n\nIf the child of the used model is a Variable Node, the `value` type will smartly be `~'ModelLikeDbData\u003cchild\u003e'` ( = if your model is stores/$storeId/... and you call stores._push(), the type annotation will be the $storeId type)\n\n```typescript\n// E.g.:\nconst newStoreId = (await stores._push({\n  name: 'Cool Store',\n  rating: 4.2,\n  open: true,\n  users: {\n    [aUserId]: {\n      name: theUserName\n    };\n  }\n})).key! // ! because the type of Reference.key is (string | null), but we know that in this case it is a string\n\nstores.$storeId.name._ref(newStoreId).set('New Name!') // Changes 'Cool Store' to 'New Name!'\n```\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e \\._clone (vars?: string | string[], database?: Database, blockDatabase: boolean = false) =\u003e Node \u003c/h3\u003e\u003c/b\u003e\n\nDeep clones the Model Node applying `vars` to the '`\\$`' keys to the new cloned model `._path`. Useful for not having to pass the `vars` all the time to a Model that you will use for a while, like having it in a Class.\n\n`database` and `blockDatabase` works as the same `_root` parameters.\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e\n._database : Database | undefined\n\u003c/h3\u003e\u003c/b\u003e\n\nIf you passed the `database` argument in `_root()` or in `_clone()`, it will be set in this property.\n\nYou probably won't have to use this.\n\n\n\u003cbr/\u003e\n\n\u003cb\u003e\u003ch3\u003e\n._blockDatabase : boolean\n\u003c/h3\u003e\u003c/b\u003e\n\nIf you passed the `blockDatabase` argument in `_root()` or in `_clone()`, it will be set in this property.\n\nYou probably won't have to use this.\n\n\u003cbr/\u003e\n\n\u003cbr/\u003e\n\n# Roadmap\n\n- Optional properties. For now, you may use the type null and pass a null value. For VarNodes, you may pass an empty object.\n\n- Optional database key; it would use the property key as the database key, getting them on finishModel().\n\n- Firestore support. Easy to add, but I don't think I will ever use Firestore again (its max 1 write per second is a big limitation).\n\n- Code tests\n\n- Check if there is a child with the same DB key\n\n- Improve this README\n\n- Typescript sourcery to know how many `vars` are needed for current node DB op\n\n- ._updateChild() will from the given object construct the key / path and only update the given child.\n\n- If blockDatabase == true, hide `database` property from `._ref()` based functions.\n\n- ._onceVal() overload with implicit 'value'.\n\n- Automatically add general use references like `'.info/connected'` to the `_root()` Node (https://firebase.google.com/docs/database/web/offline-capabilities#section-connection-state)\n\n- Check if path === '' on `._ref`-like functions (= not called `_root()`)\n\n\u003cbr/\u003e\n\n# Attention!\n\n### Model object reuse\n\nDon't use the same model object in more than one place! See example.\n\n```typescript\nconst modelObj = {\n  prop: _\u003cstring\u003e('prop')\n};\nconst root = _root({\n  model1: _('segment1', model),\n  model2: _('segment2', model),\n})\nroot.model1.prop._path()\n// This will return /segment2/prop instead of /segment1/prop, because the path\n// applied to model2.prop was also applied to model1.prop, as they are the same object.\n// This \"same path\" behavior applies to any DB operation you would do.\n```\n\nTo avoid this issue, you can either create a modelObj2 with the same content, or use the `deepClone` function that this package exports. It just deep clones an object.\n\n\n### Not allowed characters\n\nYour Realtime Database paths / segments must not include '$' char and it's not recomended to a segment to start with an '_', as those chars are specially treated by this package.\n\nFor this package model keys, only the '_' recommendation remains ( = you may use the '$'. Actually, is recommended to use it to indicate that it is a Variable Node).\n\nIDs generated by Firebase Auth and Realtime Database reference.push() don't include '$' and doesn't start with an '_'.\n\n# [Changelog](https://github.com/SrBrahma/Firebase-Database-Modeler/blob/master/CHANGELOG.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fftzi%2Ffirebase-database-modeler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fftzi%2Ffirebase-database-modeler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fftzi%2Ffirebase-database-modeler/lists"}