{"id":22330635,"url":"https://github.com/rodrigobertotti/firebase-cloud-functions-typescript-example","last_synced_at":"2025-07-29T19:32:50.242Z","repository":{"id":210983439,"uuid":"727912096","full_name":"RodrigoBertotti/firebase-cloud-functions-typescript-example","owner":"RodrigoBertotti","description":"A Node.js REST API example for Firebase, built with TypeScript, Express, Firebase Authentication, Firebase Admin SDK, and Firestore. It also handles Event Triggers (2nd gen) so all your code is organized. This project fits well to be used as a template for the creation of new servers.","archived":false,"fork":false,"pushed_at":"2024-06-17T15:36:40.000Z","size":189,"stargazers_count":9,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"dev","last_synced_at":"2024-06-17T17:42:10.942Z","etag":null,"topics":["api","cloud-functions","express","firebase","firebase-auth","firebase-auth-rest-api","firebase-authentication","firebase-functions","google-cloud","google-cloud-functions","node","nodejs","nodejs-api","nodejs-typescript","nodejs-typescript-template","rest-api","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/RodrigoBertotti.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-12-05T20:44:21.000Z","updated_at":"2024-06-17T15:36:44.000Z","dependencies_parsed_at":"2024-03-04T22:25:41.334Z","dependency_job_id":"5a3687a0-727a-42c9-b90d-a6873535067d","html_url":"https://github.com/RodrigoBertotti/firebase-cloud-functions-typescript-example","commit_stats":null,"previous_names":["rodrigobertotti/firebase-cloud-functions-typescript-example"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RodrigoBertotti%2Ffirebase-cloud-functions-typescript-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RodrigoBertotti%2Ffirebase-cloud-functions-typescript-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RodrigoBertotti%2Ffirebase-cloud-functions-typescript-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RodrigoBertotti%2Ffirebase-cloud-functions-typescript-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RodrigoBertotti","download_url":"https://codeload.github.com/RodrigoBertotti/firebase-cloud-functions-typescript-example/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228041290,"owners_count":17860221,"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":["api","cloud-functions","express","firebase","firebase-auth","firebase-auth-rest-api","firebase-authentication","firebase-functions","google-cloud","google-cloud-functions","node","nodejs","nodejs-api","nodejs-typescript","nodejs-typescript-template","rest-api","typescript"],"created_at":"2024-12-04T04:07:26.532Z","updated_at":"2024-12-04T04:07:27.143Z","avatar_url":"https://github.com/RodrigoBertotti.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Complete Firebase Cloud Functions TypeScript Example\n\nA Node.js REST API example for Firebase,\nbuilt with TypeScript, Express, Firebase Authentication, Firebase Admin SDK, and Firestore.\nIt also handles Event Triggers (2nd gen) so all your code is organized.\nThis project fits well to be used as a template for the creation of new servers.\n\nThe main aspects of this sample are:\n\n-  **An API HTTP Trigger:**\n \n   - A well-organized API under the `api` folder\n   - Access Control: Reject user access by simply choosing what user roles can access a specific path or easily check the claims with a custom `request` object in the Request Handler\n   - Reject a request anywhere by throwing `new HttpResponseError(status, codeString, message)`\n- **Events Triggers (2nd gen):** \n   - A well-organized Events Triggers under the `event-triggers` folder\n\n- Shared components between API and Event Triggers are under the `core` folder\n\n## About this example\n\nThis example is a good start if you are building a \nFirebase Cloud Functions project.\n\n### About the 2nd gen event triggers example\n\nEvery time a user or product is created, or a product is updated,\na new record is created in\nthe `db-changes` Firestore Collection that only admins can access,\nthe code for these triggers is inside the `event-triggers` folder.\n\nThe triggers are `onUserCreated`, `onProductCreated`, and `onProductUpdated`.\n\n### About the `api` HTTP trigger\n\nThere are three roles: `storeOwner`, `buyer` and `admin`.\nAnyone can create an account, but an `adminKey` is required to create \na user with `admin` role.\n\n#### What each user can do\n\nStore Owners:\n  - ✅ Create products\n  - ✅ List public products data\n  - ✅ Get full data of his own product\n  - ❌ Get full data of other store owners' product\n  - ❌ List records of changes made inside the DB, like \"Product Blouse has been updated\"\n\nBuyers:\n\n- ✅ List public products data\n- ❌ Create products\n- ❌ Get full data of a product\n- ❌ List records of changes made inside the DB, like \"Product Blouse has been updated\"\n\nAdmins: \n\n- ✅ Create products\n- ✅ List public products data\n- ✅ Get full data of ANY product\n- ✅ List records of changes made inside the DB, like \"Product Blouse has been updated\"\n\n## Getting Started\n\nIn the Firebase Console:\n \n1. Go to Build \u003e Authentication \u003e Get Started \u003e Sign-in method \u003e Email/Password and enable Email/Password and save it.\n\n2. Also go to Build \u003e Firestore Database \u003e Create database. You can choose the option `Start in test mode`\n\n### Deploying\n\nGo to the `functions` folder and run `npm install`\nto install the dependencies. After that,\ngo back to the root folder (`cd ..`) and run:\n\n- `npm install -g firebase-tools` to install the Firebase CLI\n- `firebase use --add` and select your Firebase project, add any alias you prefer\n- And finally, run `firebase deploy`\n\n## API Authentication\n\nFirebase Authentication is used to verify\nif the client is authenticated on Firebase Authentication,\nto do so, the client side should inform the `Authorization` header:\n\n### `Authorization` Header\n\nThe client's ID Token on Firebase Authentication in the format `Bearer \u003cidToken\u003e`,\nit can be obtained on the client side after the authentication is performed with the\nFirebase Authentication library for the client side. \nIt can be generated by the client side only.\n\n#### Option 1: Generating ID Token with Postman:\n\nFollow the previous instructions on [Use Postman to test it](#using-postman-to-test-it) and pass\nit as `Authorization` header value in the format `Bearer \u003cidToken\u003e`\n\n#### Option 2: Generating ID Token with a Flutter Client:\n```dart\nfinal idToken = await FirebaseAuth.instance.currentUser!.getIdToken();\n// use idToken as `Authorization` header value in the format \"Bearer \u003cidToken\u003e\"\n```\n\n#### Option 3: Generating ID Token with a Web Client:\n```javascript\nconst idToken = await getAuth(firebaseApp).currentUser.getIdToken();\n// use idToken as `Authorization` header value in the format \"Bearer \u003cidToken\u003e\"\n```\n\n## Testing\n\n### Option 1: Testing with Remote DB\nTo make tests remotely, check what is your **remote** functions URL: in the Firebase Console go\nto **Functions** \u003e and check the `api` url, it ends with `.cloudfunctions.net/api`.\n\n### Option 2: Testing Locally with a local emulator\n\nIn case you want to make tests locally using the Firebase Emulator,\nyou can run `npm run emulator` inside the `functions` folder.\n\n⚠️ Changes in the local emulator\n**won't affect the remote db.**\n\nOpen the Emulator UI\non http://127.0.0.1:3005 \u003e **Functions emulator** \u003e and on the first lines\ncheck the `http function initialized...` log, it shows your Local URL, it ends with `/api`.\n\n### Using Postman to test it\n\n**1.** Import the **[postman_collection.json](postman_collection.json)** file to your Postman\n\n**2.** Right-click on the Postman collection you previously imported,\n   click on **Edit** \u003e **Variables** and on **api** replace the **Current Value** with\n   your API URL. \n\nMake sure the URL **ends with** `/api` and remember that if you use the local\n   emulator URL it won't affect the remote db.\n\n   If you are testing using the local emulator, it will look something like: `http://127.0.0.1:\u003cport\u003e/\u003cyour-project-id\u003e/\u003cregion\u003e/api`\n\n   But if you are testing using the remote db, it will look like: `https://\u003cyour-project-id\u003e.cloudfunctions.net/api`\n\n**3.** Create an account on the `1. Create Account` Postman Request\n\n**4.** Follow the login steps to get an ID Token on Postman:\n   \n   *It's better to use a library of Firebase Authentication on the Client Side\n   to get the ID Token, but let's use this method for testing because we are using Postman only*\n\n- **4.1.** In the Firebase Console \u003e Go to Project Overview and Add a new **Web** platform\n\n- **4.2.** Add a Nickname like \"Postman\" and click on Register App\n\n- **4.3.** Copy only the **apiKey** field inside the `firebaseConfig` object\n\n-  **4.4** Let's get the Firebase Authentication Token, on Postman, go to `2. Login on Google APIS` request\n   example and pass the `apiKey` as Query Param, edit the body with your email and password and click on **Send**, \nyou will obtain an `idToken` as the response.\n\n-  **4.5** For the other requests, the `idToken` should be set in the `Authorization` header (type **Bearer**).\n   Let's set it as Postman variable too, so right-click on the Postman collection \n   **Edit** \u003e **Variables** and on **idToken** replace the **Current Value** with\n   the user **idToken** you previously obtained.\n\n## Access Control\n\nThis project uses custom claims on Firebase Authentication to\ndefine which routes the users have access to.\n\n### Define custom claims to a user\n\nThis can be done in the server like below:\n```javascript\nawait admin.auth().setCustomUserClaims(user.uid, {\n    storeOwner: true,\n    buyer: false,\n    admin: false\n});\n```\n### Configuring the routes\n\nYou can set a param (array of strings) on the `httpServer.\u003cmethod\u003e`\nfunction, like:\n\n```javascript\nhttpServer.get (\n    '/product/:productId/full-details', \n    this.getProductByIdFull.bind(this), ['storeOwner']\n);\n```\n\nIn the example above, only users with the `storeOwner` custom claim will\nhave access to the `/product/:productId/full-details` path.\n\nIs this enough? Not always, so let's check the next section [Errors and permissions](#errors-and-permissions).\n\n## API Errors and permissions\n\nYou can easily send an HTTP response with code between 400 and 500 to the client\nby simply throwing a `new HttpResponseError(...)` on your controller, service or repository,\nfor example:\n\n```javascript\nthrow new HttpResponseError(400, 'BAD_REQUEST', \"Missing 'name' field on the body\");\n```\n\nSometimes defining roles isn't enough to ensure that a user can't \naccess or modify a specific data,\nlet's imagine if a store owner tries to get full details\nof a product he is not selling, like a product of another store owner,\nhe still has access to the route because of his `storeOwner` custom claim,\nbut an additional verification is needed.\n\n```javascript\nif (product.storeOwnerUid != req.auth!.uid) {\n    throw new HttpResponseError(\n        403, \n        'FORBIDDEN', \n        `You aren't the correct storeOwner`\n    );\n}\n```\n\n### 🚫 Permission errors\n\n- #### \"Only storeOwner can perform this operation\"\nMeans you are not logged in with a user that has the `buyer` claim rather\nthan with a user that contains the  `storeOwner` claim.\n\n- #### \"You aren't the correct storeOwner\"\nMeans you are logged in with the correct claim, but you are trying to read other storeOwner's data.\n\n- #### \"Only admin can perform this operation\"\nMeans that this operation requires to be logged with\na user that has the `admin` claim, but the current user hasn't.\n\n- #### \"Requires authentication\"\nIf you forget to add the Authentication header\n\n## Authentication fields on Express Request Handler\n\nThis project adds 3 new fields to the request object on the \nexpress request handler, \nyou can also customize this on `src/api/@types/express.d.ts` TypeScript file.\n\n### `req.authenticated` \ntype: `boolean`\n\nIs true only if the client is authenticated, which means, the client\ninformed `Authorization` on the headers, and these\nvalues were successfully validated.\n\n### `req.auth` \ntype: [UserRecord](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.userrecord) | `null`\n\nIf authenticated: Contains user data of Firebase Authentication.\n\n### `req.token` \ntype: [DecodedIdToken](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken) | `null`\n\nIf authenticated: Contains token data of Firebase Authentication.\n\n## Getting in touch\n\nFeel free to open a GitHub issue about:\n\n- :grey_question: questions\n\n- :bulb: suggestions\n\n- :ant: potential bugs\n\n## License\n\n[MIT](LICENSE)\n\n## Reference\n\nThis project used as reference part of the structure of the GitHub project [node-typescript-restify](https://github.com/vinicostaa/node-typescript-restify).\nThank you [developer](https://github.com/vinicostaa/)!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodrigobertotti%2Ffirebase-cloud-functions-typescript-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frodrigobertotti%2Ffirebase-cloud-functions-typescript-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodrigobertotti%2Ffirebase-cloud-functions-typescript-example/lists"}