{"id":15294843,"url":"https://github.com/firebase-toolbelt/firebase-rules","last_synced_at":"2025-10-07T06:31:19.309Z","repository":{"id":57236666,"uuid":"68952015","full_name":"firebase-toolbelt/firebase-rules","owner":"firebase-toolbelt","description":"Create readable and scalable firebase rules using javascript.","archived":true,"fork":false,"pushed_at":"2023-08-18T13:26:02.000Z","size":112,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-20T09:26:48.285Z","etag":null,"topics":["firebase","javascript","node","rules"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/firebase-toolbelt.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":"2016-09-22T18:49:36.000Z","updated_at":"2023-09-14T15:22:21.000Z","dependencies_parsed_at":"2024-09-30T17:07:35.926Z","dependency_job_id":"2ff98b5f-77ba-452f-85af-3607b77c49fa","html_url":"https://github.com/firebase-toolbelt/firebase-rules","commit_stats":null,"previous_names":["tasking/firebase-rules"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase-toolbelt%2Ffirebase-rules","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase-toolbelt%2Ffirebase-rules/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase-toolbelt%2Ffirebase-rules/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase-toolbelt%2Ffirebase-rules/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/firebase-toolbelt","download_url":"https://codeload.github.com/firebase-toolbelt/firebase-rules/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235599951,"owners_count":19016191,"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":["firebase","javascript","node","rules"],"created_at":"2024-09-30T17:07:31.748Z","updated_at":"2025-10-07T06:31:18.906Z","avatar_url":"https://github.com/firebase-toolbelt.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e ⚠️ **Important** ⚠️\n\u003e This repository is not actively maintained so feel free to fork it and improve it. I've not been using Firebase for years - so I'm not motivated to continue maintaining libraries around it.\n\n---\n\n\u003ca align=\"center\" href=\"https://github.com/firebase-toolbelt/firebase-toolbelt\"\u003e\u003cimg width=\"100%\" src=\"https://image.ibb.co/iWSg8k/Firebase_Rules.png\" alt=\"Firebase Rules\" border=\"0\" /\u003e\u003c/a\u003e\n\n\u003e Make your Firebase database rules readable and scalable. Using only javascript.\n\n## Table of Contents\n\n[Getting Started](#getting-started)\n\n[Helpers](#helpers)\n- [Conditions](#conditions)\n- [CRUD](#crud)\n- [Common](#common)\n- [New Data Root](#new-data-root)\n\n[Testing](#testing)\n\n## Getting Started\n\nThe best way to understand how it works it's just by using it. Let's try it out in this quick demo.\nLet's create some rules for an app that let users create their own 'user' object and then create/update their own posts.\n\nThe database should look something like this:\n\n```json\n{\n  \"users\": {\n    \"$userId\": {\n      \"firstName\": \"John\"\n    }\n  },\n  \"posts\": {\n    \"$postId\": {\n      \"title\": \"My post title.\",\n      \"body\": \"My post body.\",\n      \"createdAt\": \"timestamp\",\n      \"createdBy\": \"$userId\"\n    }\n  }\n}\n```\n\nLet's first create our rules related to the user object.\nThe user must be able to create and update his profile.\nOther users must be able to see his profile.\nNo users are ever removed from our app.\n\n```javascript\n// ./rules/modules/users.js\n\nconst { ifCondition } = require('firebase-rules/helpers/conditions');\nconst {\n  isAuth,\n  isAuthId,\n  newDataExists,\n  hasChildren,\n  isString,\n  validate\n} = require('firebase-rules/helpers/common');\n\nconst isUserAndIsNotRemoving = ifCondition(\n  newDataExists,\n  isAuthId('$userId'),\n  false\n);\n\nmodule.exports = {\n  'users/$userId': {\n    read: isAuth,\n    write: isUserAndIsNotRemoving,\n    validate: hasChildren(['firstName'])\n  },\n  'users/$userId/firstName': validate(isString),\n  'users/$userId/$invalidProp': validate(false)\n});\n```\n\nNext we can build our post object rules.\nAny user can create posts. Only the post's creator may update or remove it.\nPosts can be read by any of our app's users.\n\n```javascript\n// ./rules/modules/posts.js\n\nconst {\n  isAuth,\n  isAuthId,\n  isString,\n  isNow,\n  newProp,\n  hasChildren,\n  validate\n} = require('firebase-rules/helpers/common');\n\nmodule.exports = {\n  'posts/$postId': {\n    read: isAuth,\n    write: isAuthId(newProp('createdBy')),\n    validate: hasChildren(['title', 'body', 'createdAt', 'createdBy'])\n  },\n  'posts/$postId/title': validate(isString),\n  'posts/$postId/body': validate(isString),\n  'posts/$postId/createdAt': validate(isNow),\n  'posts/$postId/createdBy': validate(isAuthId(newData)),\n  'posts/$postId/$invalidProp': validate(false)\n};\n```\n\nNow let's export this rules so we can import them to firebase.\n\n```javascript\n// ./rules/index.js\n\nconst createRules = require('firebase-rules');\n\ncreateRules({\n  ...require('./modules/users'),\n  ...require('./modules/posts')\n}, 'database.rules.json');\n\n```\n\nDone! There is now a file named `database.rules.json` in our app's root folder.\nWe can now upload our app to Firebase using their CLI or even upload our file manually using their website.\n\n## Helpers\n\nWe include many helpers that are commonly used when building a firebase ruleset.\n**Keep in mind that we're only generating strings. So you can quickly create helpers of your own. Check out our helpers source code, they're so simple it's ridiculous.**\n\n### Conditions\n\nUsing our conditions helpers will make writing your code more like writing normal code logic.\n\n#### ifCondition\n\nIf the first condition is true, the second condition is checked.\nIf the first condition is false, the third condition is checked.\nIt works much like a ternary operator.\n\nIf the third condition is not provided, it defaults to 'false'.\n\n```javascript\nifCondition(\n  ifUserIsAuth,\n  alsoCheckIfHeIsValid,\n  otherwiseCheckOtherCondition\n)\n```\n\n#### anyCondition\n\nAny of the passed conditions must be true for the rule to be accepted.\n**It might also receive an array instead of a list of arguments**.\n\n```javascript\nanyCondition(\n  thisMustBeTrue,\n  OR_thisMustBeTrue,\n  OR_atLeastThisMustBeTrue,\n  ...\n)\n```\n\n#### everyCondition\n\nAny of the passed conditions must be true for the rule to be accepted.\n**It might also receive an array instead of a list of arguments**.\n\n```javascript\neveryCondition(\n  thisMustBeTrue,\n  AND_thisTooMustBeTrue,\n  AND_thisMustAlsoBeTrue,\n  ...\n)\n```\n\n### CRUD\n\nOur CRUD helpers makes it easy to check for different rules in case of create/update/delete situations.\n\n```javascript\nonCreate( checkIfAllChildrenArePresent ),\nonUpdate( checkIfNoRequiredChildrenAreRemoved ),\nonDelete( checkIfUserCanDeleteThis )\n```\n\nNot that `onDelete` is not called on `validate` rules since firebase bypasses validations on null values.\n\n### Common\n\nWe also provide a lot of common snippets so you won't have to redo the basics.\n\n**auth**\n\n```javascript\nisAuth\nisAuthId(any)\n```\n\ne.g. any authenticated user can read any post but only the owner can update it.\n\n```javascript\n'posts/$userId/$postId':\n  read: isAuth                // 'auth.uid != null',\n  write: isAuthId('$userId')  // 'auth.uid == $userId'\n```\n\n**data**\n\n```javascript\ndata\nisData(any)\ndataExists\ndataIsEmpty\n\nnewData\nisNewData(any)\nnewDataExists\nnewDataIsEmpty\n```\n\ne.g. the path can only be created but not updated\n\n```javascript\n$path:\n  write: everyCondition( dataIsEmpty, newDataExists )\n  // data.val() == null \u0026\u0026 newData.val() != null\n```\n\n**props**\n\n```\nprop(any) | child(any)\nnewProp(any) | newChild(any)\nhasProp([]) | hasChildren([])\n```\n\ne.g. a post can only be created with all required fields filled. the createdBy must hold the userId.\n\n```javascript\npost:\n  write: everyCondition(\n    hasChildren(['title', 'body', 'createdBy']),\n    isAuthId(newProp('createdBy'))\n  )\n  // newData.hasChildren(['title', 'body', 'createdBy']) \u0026\u0026 newData.child('createdBy').val() == auth.uid\n```\n\n**validation**\n\n```javascript\nvalidate(conditions)\n\nisString\nisNumber\nisInteger\nisBoolean\nisNow\n```\n\nA lot of paths will only hold validation rules so there's a short-hand function that helps with that.\n\n```javascript\nposts/$postId/title:     validate(isString)\nposts/$postId/likes:     validate(isNumber)\nposts/$postId/archived:  validate(isBoolean)\nposts/$postId/createdAt: validate(isNow)\nposts/$postId/createdBy: validate(isAuthId(newData))\n```\n\n**indexing**\n\n```\nindexOn([string])\n```\n\ne.g. indexing some children for optimized querying\n\n```\nposts/$postId:\n  indexOn: ['createdAt', 'createdBy']\n```\n\n**user defined strings**\n\n```javascript\ns('something') // '\\'something\\''\n```\n\nSince we're dealing with an object that will be turned to a json, sometimes it's useful escaping user defined strings so they're not mistaken for variables by the firebase rules parser. This is normally used when passing a user defined string to a function like so:\n\n```javascript\nconst userExists = userId =\u003e `root.child('users').child(${userId}).exists()`;\n\nuserName('$userId') // `root.child('users').child($userId).exists()`\nuserName('123')     // `root.child('users').child(123).exists()` -\u003e ERROR -\u003e `123` is not a valid variable\nuserName(s(123))    // `root.child('users').child('123').exists()`\n```\n\n**transformers**\n\n```javascript\ntoData(string | function)\ntoNewData(string | function)\n```\n\nThere will be a time you will want to duplicate a rule so it checks both data and newData.\nThis transformers will help you building code without having to duplicate it in these situations.\n\n```javascript\nconst userExists = (userId) =\u003e `root.child('users').child(${userId}).exists()`;\nconst userWillExist = toNewData(userExists);\n\nuserExists(child('createdBy')) // root.child('users').child(data.child('createdBy').val()).exists()\nuserWillExist('$userId') // newDataRoot().child(newData.child('createdBy').val()).exists()\n```\n\nSee the `newDataRoot()` that appears on the output above? Read below to understand it better.\n\n## New Data Root\n\nTurns out you can't really use the `root` variable when you're dealing with the data that is being added to your database.\nThis can be a bit of a pain when you're defining reusable rules that will be used on a lot of different paths.\n\nLet me show you what I mean.\n\n```javascript\nusers/$userId/numberOfPosts:\n  validate: 'root.child('posts').child('$userId').numChildren() == newData.val()'\n```\n\nThe above code would work perfectly if you are increasing the `user/numberOfPosts` prop *after* you've already created the post on the database. But if you're creating the post at the same time you're updating this counter it would not really work. `root` can't be used in the context of the data that is being added. To solve this you would have to do something like this:\n\n```javascript\nusers/$userId/numberOfPosts:\n  validate: 'newData.parent().parent().child('posts').child('$userId').numChildren() == newData.val()'\n```\n\nMessy, right? Not only that, you won't be able to reuse any rule throughout your application because things may be at different depths, so they will require a different number of `parent()` to be called.\n\nFear not. We've got your back. While we're parsing your rules, we will check for a special keyword `newDataRoot()` and we will replace it with the correct code regarding the path you're using it. Let's see it in action.\n\n```javascript\nconst numberOfPosts = userId =\u003e `newDataRoot().child('posts').child(${userId}).numChildren()`;\n\nusers/$userId\n  validate: `${numberOfPosts('$userId')} == newData.child('numberOfPosts').val()'\n// 'newData.parent().child('posts').child('$userId').numChildren() == newData.val()'\n\nusers/$userId/numberOfPosts:\n  validate: `${numberOfPosts('$userId')} == newData.val()'\n// 'newData.parent().parent().child('posts').child('$userId').numChildren() == newData.val()'\n\n```\n\nThis is *really* useful when you're creating data on multiple locations that depend on each other.\n\n## Testing\n\nWe recommend using the excelent [targaryen](https://github.com/goldibex/targaryen) library for testing your firebase rules without reaching for the server. Using it with **firebase-rules** is extremelly easy as you can generate your rules as an object instead of creating them on a file by just omitting the filename when calling the `createRules` function.\n\n```javascript\nconst targaryen = require('targaryen');\nconst chai, { expect } = require('chai');\nchai.use(targaryen);\n\nconst createRules = require('firebase-rules');\nconst { anyCondition } = require('firebase-rules/helpers/conditions');\n\n/**\n * You can retrieve your rules as an object\n * by just omitting the filename argument.\n */\n \nconst myFirebaseRules = createRules({\n  '/my/path': {\n    validate: anyCondition(\n      `newData.val() == 'a'`,\n      `newData.val() == 'b'`\n    )\n  }\n});\n\n/**\n * an object representing your firebase data\n */\n \nconst myFirebaseData = {};\n\n/**\n * Now you can test your rules!\n */\n\ndescribe('my firebase rules tests', function() {\n\n  before(function() {\n    targaryen.setFirebaseData(myFirebaseData);\n    targaryen.setFirebaseRules(myFirebaseRules);\n  });\n\n  it('should only allow 'a' or 'b' values to be written to `/my/path`', function() {\n    expect(null).can.write('a').to.path('/my/path');\n    expect(null).cannot.write('c').to.path('/my/path');\n  })\n\n});\n```\n\n---\n\nMade with ♥ by [Georges Boris](https://georgesboris.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffirebase-toolbelt%2Ffirebase-rules","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffirebase-toolbelt%2Ffirebase-rules","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffirebase-toolbelt%2Ffirebase-rules/lists"}