{"id":6339,"date":"2020-05-24T21:32:50","date_gmt":"2020-05-24T21:32:50","guid":{"rendered":"https:\/\/max-drake.cc\/?p=6339"},"modified":"2020-05-24T21:32:54","modified_gmt":"2020-05-24T21:32:54","slug":"building-a-crud-restful-api-with-cloud-functions-and-firestore-tutorial-by-soren-spangsberg-jorgensen","status":"publish","type":"post","link":"https:\/\/max-drake.cc\/?p=6339","title":{"rendered":"Building a CRUD RESTful API with Cloud Functions and Firestore tutorial by  S\u00f8ren Spangsberg J\u00f8rgensen"},"content":{"rendered":"\n<p>Having a self created API allows me to hook up data to connect to front end pages, so I can feed information from the API to the page. <\/p>\n\n\n\n<p>This tutorial is CRUD tutorial with firebase firestore and functions , so another item that I&#8217;m very interested in. <\/p>\n\n\n\n<p><strong><a rel=\"noreferrer noopener\" href=\"https:\/\/www.youtube.com\/watch?v=3dFT7QaVSZ8&amp;list=PLJetLDY7yKupm5WTx02ylh1I25rJLPvXe\" target=\"_blank\">This is the video series<\/a><\/strong> where S\u00f8ren Spangsberg J\u00f8rgensen goes through the setup\/testing steps to get it running.<\/p>\n\n\n\n<p>This is something interesting that will hopefully link up with some of the Static websites, being able to have an API up and running. This is also something that I have been wanting to do for a while and have been sidetracked down a lot of paths on the way. <\/p>\n\n\n\n<p>As I was coding from the screen I made a few mistakes and it took me two days to get up and running, but I now have an operational localhost\/postman workflow from the Tutorial. Now I need to adapt it to an expenses app. <\/p>\n\n\n\n<h4 class=\"has-vivid-red-color has-text-color wp-block-heading\">Project process<\/h4>\n\n\n\n<p>I&#8217;ve setup the Firebase project, created a database (no data in it as yet)<\/p>\n\n\n\n<p>Setup an App (no data) and used VS Studio to run a&gt; firebase init and create a functions setup. <\/p>\n\n\n\n<p>I&#8217;m now onto video 4 , having got the &#8220;Hello World&#8221; and initial test route &amp; started the server on locval host to test. I&#8217;d done this thismorning and pushed to Github, the surface tablet had issues with linking to Github and I had to donload Git Bash on that pc and set up a new SSH key. Then I could push data to Github that I&#8217;ve now pulled on my laptop. <\/p>\n\n\n\n<p>So on with Tut 4: This we&#8217;ll set up authentication between app and firebase firestore. <\/p>\n\n\n\n<figure class=\"wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe title=\"Building a RESTful API with Cloud Functions and Firestore - 04 - Create Route and Service Account\" width=\"678\" height=\"381\" data-src=\"https:\/\/www.youtube.com\/embed\/dKoQnNylxm8?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" class=\"lazyload\" data-load-mode=\"1\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>So setup authentication and add it to index.html. Generate a key , which is a json file, rename it something like &#8220;permissions.json&#8221; and put it in the functions folder of the project where the index.js file is and add its path to the index.js file:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"567\" data-src=\"https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-1024x567.jpg\" alt=\"\" class=\"wp-image-6340 lazyload\" data-srcset=\"https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-1024x567.jpg 1024w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-300x166.jpg 300w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-768x425.jpg 768w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-1536x851.jpg 1536w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-50x28.jpg 50w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-90x50.jpg 90w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-100x55.jpg 100w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-181x100.jpg 181w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-1155x640.jpg 1155w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-640x355.jpg 640w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-1386x768.jpg 1386w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-12_29_15-crudApi-\u2013-Firebase-console-scaled.jpg 1600w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/567;\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>const admin = require(\"firebase-admin\");\n\nconst serviceAccount = require(\".\/permissions.json\");\n\nadmin.initializeApp({\n  credential: admin.credential.cert(serviceAccount),\n  databaseURL: \"https:\/\/XXXXX.firebaseio.com\",\n});<\/code><\/pre>\n\n\n\n<p>Next, for the create function need the script  and then test it in postman using the local host server up and running and adding the extended path <strong>http:\/\/localhost:5001\/crudapi-2aee7\/us-central1\/app\/<span style=\"color:#cf2e2e\" class=\"color\">api\/create<\/span><\/strong><\/p>\n\n\n\n<p>This is something that confuses me. <span style=\"color:#cf2e2e\" class=\"color\"><span style=\"background-color:#fcb900\" class=\"background-color\">The functions are on a <strong>\/us-central1<\/strong> <\/span><\/span>server<span style=\"color:#cf2e2e\" class=\"color\"><span style=\"background-color:#cf2e2e\" class=\"background-color\"> <\/span>whereas the database is Asia<\/span>. I think I may need to just bang them all on a us server to keep them all in one place. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"833\" data-src=\"https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-1024x833.jpg\" alt=\"\" class=\"wp-image-6341 lazyload\" data-srcset=\"https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-1024x833.jpg 1024w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-300x244.jpg 300w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-768x625.jpg 768w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-1536x1250.jpg 1536w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-50x41.jpg 50w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-61x50.jpg 61w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-100x81.jpg 100w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-123x100.jpg 123w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-786x640.jpg 786w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-640x521.jpg 640w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-944x768.jpg 944w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-1327x1080.jpg 1327w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-24-13_01_22-Postman-scaled.jpg 1600w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/833;\" \/><\/figure>\n\n\n\n<p>I couldn&#8217;t get the post (create to work, and tried putting it onto an Asia server, that didn&#8217;t work, so I&#8217;ve made a collection in the database and added a document to test a READ (get request). <\/p>\n\n\n\n<p>I rebuilt a new project, the local host emulator recognises the permissions key so its authorised. See below:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"908\" height=\"671\" data-src=\"https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code.jpg\" alt=\"\" class=\"wp-image-6342 lazyload\" data-srcset=\"https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code.jpg 908w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code-300x222.jpg 300w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code-768x568.jpg 768w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code-50x37.jpg 50w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code-68x50.jpg 68w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code-100x74.jpg 100w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code-135x100.jpg 135w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code-866x640.jpg 866w, https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-10_30_23-index.js-crudApi3-Visual-Studio-Code-640x473.jpg 640w\" data-sizes=\"(max-width: 908px) 100vw, 908px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 908px; --smush-placeholder-aspect-ratio: 908\/671;\" \/><\/figure>\n\n\n\n<p>I&#8217;m still getting timeout on the database, so no create or read functions still, even on deploy. <\/p>\n\n\n\n<h4 class=\"has-vivid-red-color has-text-color wp-block-heading\">The problem:<\/h4>\n\n\n\n<p>I was not putting opening brackets around the asyc function and at the end })(); having the opening\/closing brackets, which is a react thing and I think it activates the function. Anyway all done now, so I can get it all working.  <\/p>\n\n\n\n<p>So: I now have an active script that works with the firebase.firestore . I&#8217;ve also created an expenses database api. I&#8217;ve deployed the cloud functions and tested them, they are fine. <\/p>\n\n\n\n<p>I was wondering how to get info out of database, but I can just do a read on the collection and then, in postman cut\/paste JSON into a JSON\/CSV converter to put into something else. Crude, but a simple backup. There are more sophisticated methods, but that will do for now. The code for the ttorial below for the<strong> index.js<\/strong> file in the <strong>Functions<\/strong> directory:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const functions = require(\"firebase-functions\");\nconst admin = require(\"firebase-admin\");\n\nconst serviceAccount = require(\".\/permissions.json\");\n\nadmin.initializeApp({\n  credential: admin.credential.cert(serviceAccount),\n  databaseURL: \"https:\/\/crudapi3.firebaseio.com\",\n});\nconst db = admin.firestore();\nconsole.log(db);\n\nconst express = require(\"express\");\nconst app = express();\n\nconst cors = require(\"cors\");\napp.use(cors({ origin: true })); \/\/ this will allow it to go to another domain\n\n\/\/routes\napp.get(\"\/hw\", (req, resp) => {\n  return resp.status(200).send(\"Hello World\");\n});\n\/\/crud\n\/\/create\napp.post(\"\/api\/create\", (req, resp) => {\n  (async () => {\n    try {\n      await db\n        .collection(\"product\")\n        .doc(\"\/\" + req.body.id + \"\/\")\n        .create({\n          name: req.body.name,\n          description: req.body.description,\n          price: req.body.price,\n        });\n      return resp.status(200).send(req.body.id + \" added to db\");\n    } catch (error) {\n      console.log(error);\n      console.log(\"this request did not work\");\n      return resp.status(500).send(error);\n    }\n  })();\n});\n\n\/\/read one doc\napp.get(\"\/api\/read\/:id\", (req, resp) => {\n  (async () => {\n    try {\n      const document = db.collection(\"product\").doc(req.params.id);\n      let product = await document.get();\n      let response = product.data();\n      return resp.status(200).send(response);\n    } catch (error) {\n      console.log(error);\n      console.log(\"this request did not work\");\n      return resp.status(500).send(error);\n    }\n  })();\n});\n\n\/\/read all docs\napp.get(\"\/api\/read\", (req, resp) => {\n  (async () => {\n    try {\n      let query = db.collection(\"product\");\n      let response = &#91;];\n\n      await query.get().then((querySnapshot) => {\n        let docs = querySnapshot.docs; \/\/ the result of the query\n        for (let doc of docs) {\n          const selectedItem = {\n            id: doc.id,\n            name: doc.data().name,\n            description: doc.data().description,\n            price: doc.data().price,\n          };\n          response.push(selectedItem);\n        }\n        return response; \/\/ each then should return a value\n      });\n      return resp.status(200).send(response);\n    } catch (error) {\n      console.log(error);\n      return resp.status(500).send(error);\n    }\n    return console.log(\"blah\");\n  })();\n});\n\n\/\/update. Put method\napp.put(\"\/api\/update\/:id\", (req, resp) => {\n  (async () => {\n    try {\n      const document = db.collection(\"product\").doc(req.params.id);\n      await document.update({\n        name: req.body.name,\n        description: req.body.description,\n        price: req.body.price,\n      });\n      return resp.status(200).send(req.body.id + \"altered in db\"); \/\/req.body.id + \"altered in db\"\n    } catch (error) {\n      console.log(error);\n      console.log(\"this request did not work\");\n      return resp.status(500).send(error);\n    }\n  })();\n});\n\n\/\/delete\napp.put(\"\/api\/delete\/:id\", (req, resp) => {\n  (async () => {\n    try {\n      const toDeleteId = req.params.id;\n      const document = db.collection(\"product\").doc(req.params.id);\n      await document.delete();\n\n      return resp.status(200).send(toDeleteId + \" deleted in db\");\n    } catch (error) {\n      console.log(error);\n      console.log(\"this request did not work\");\n      return resp.status(500).send(error);\n    }\n  })();\n});\n\/\/export the api to the firebase cloud fnctions\nexports.app = functions.https.onRequest(app);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">End comment<\/h3>\n\n\n\n<p>That was hard yakka but I got there in the end. Two days messing with the code. I can now work on a front end interface for it, that&#8217;s another project. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Having a self created API allows me to hook up data to connect to front end pages, so I can feed information from the API to the page. This tutorial is CRUD tutorial with firebase firestore and functions , so another item that I&#8217;m very interested in. This is the video series where S\u00f8ren Spangsberg [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":6343,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[35,41,448,447,36,43,445,39],"tags":[],"class_list":["post-6339","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-api_json","category-aws-gc-az-free-tier","category-cloud-functions","category-cloud-services","category-databases","category-javascript","category-node_js","category-nosql-databases"],"featured_image_src":"https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-19_31_56-Postman.jpg","featured_image_src_square":"https:\/\/max-drake.cc\/wp-content\/uploads\/2020\/05\/2020-05-25-19_31_56-Postman.jpg","author_info":{"display_name":"Max Drake","author_link":"https:\/\/max-drake.cc\/?author=1"},"_links":{"self":[{"href":"https:\/\/max-drake.cc\/index.php?rest_route=\/wp\/v2\/posts\/6339","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/max-drake.cc\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/max-drake.cc\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/max-drake.cc\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/max-drake.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6339"}],"version-history":[{"count":0,"href":"https:\/\/max-drake.cc\/index.php?rest_route=\/wp\/v2\/posts\/6339\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/max-drake.cc\/index.php?rest_route=\/wp\/v2\/media\/6343"}],"wp:attachment":[{"href":"https:\/\/max-drake.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6339"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/max-drake.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6339"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/max-drake.cc\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6339"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}