- 00:00:01 welcome to this video great to see you
- 00:00:03 back in this miniseries in the last
- 00:00:05 videos to learn what firebase cloud
- 00:00:07 functions are and how to create a cloud
- 00:00:09 function that listens to changes in our
- 00:00:11 firebase storage in this video we'll
- 00:00:14 listen to http events hence actually
- 00:00:17 creating our own API let's dive into
- 00:00:20 that
- 00:00:23 so let's add a new cloud function here's
- 00:00:27 the cloud function I created in the last
- 00:00:29 video the one which reacts on storage
- 00:00:31 events now I'll export a new function
- 00:00:34 here with exports and then the function
- 00:00:36 name and I will name this one here
- 00:00:39 upload file I'll use the functions
- 00:00:44 object here which we get from the
- 00:00:46 firefight fire base functions package
- 00:00:48 and I will use the HTTP realm to listen
- 00:00:53 to HTTP events there we have on request
- 00:00:56 and on request then also takes a
- 00:00:59 function that gets executed and this
- 00:01:01 gets executed whenever an HTTP request
- 00:01:03 reaches this endpoint and you'll get the
- 00:01:06 exact URL once you deployed it this
- 00:01:08 object here actually will hold a request
- 00:01:12 and a response object as you know it
- 00:01:15 from Express Apps note Express so in
- 00:01:19 there we can now do something we should
- 00:01:21 absolutely send a response that's the
- 00:01:23 bare minimum so what we can do here is
- 00:01:25 we can send a status code of 200 with
- 00:01:28 maybe some JSON data where we output a
- 00:01:32 message it worked and you could also
- 00:01:37 send HTML here it doesn't have to be
- 00:01:38 JSON data and if this is a certain
- 00:01:41 extent no it's probably worth looking
- 00:01:43 into some node Express tutorials because
- 00:01:45 I'll essentially be using the same
- 00:01:47 syntax and language here so with that
- 00:01:50 I'm sending a response and now why don't
- 00:01:53 we just try it out
- 00:01:54 this is a basic HTTP function cloud
- 00:01:57 function which we can trigger by sending
- 00:01:59 a request to it so let me run firebase
- 00:02:02 deploy in our project here to put this
- 00:02:05 back into firebase and create a function
- 00:02:09 with this upload file function we just
- 00:02:13 define and then let's see how we can
- 00:02:14 trigger that deployment finished and now
- 00:02:17 if you expand the console here or scroll
- 00:02:18 up you will see where your functions are
- 00:02:21 deployed to and for HTTP functions
- 00:02:24 you'll get the endpoint you have to send
- 00:02:26 a request to so I'll copy that URL here
- 00:02:29 and then I'll simply open postman that
- 00:02:32 is a tool that allows you to send HTTP
- 00:02:35 requests well
- 00:02:37 to any endpoint you want essentially in
- 00:02:38 there I'll enter the URL and I won't set
- 00:02:43 any special headers or a body I'll just
- 00:02:45 send a get request and it's sent and now
- 00:02:48 this gives me and deep back some JSON
- 00:02:50 data it worked with status code 200 so
- 00:02:54 this shows does a dead works and we can
- 00:02:56 also send a post request to the Zen
- 00:02:58 point we get back there are the same
- 00:02:59 response if we only want to handle some
- 00:03:02 HTTP works and at all of them we have to
- 00:03:05 do this in this endpoint by default that
- 00:03:08 handles all incoming requests so now we
- 00:03:11 could say we just want to handle post
- 00:03:12 requests so we could check if request
- 00:03:15 method if this is not equal to post if
- 00:03:20 it is not equal then I will return a
- 00:03:22 response with maybe status code 500 and
- 00:03:26 set jason-2 to pass an object where the
- 00:03:30 message is not allowed or whatever you
- 00:03:32 want so with that I updated this now I
- 00:03:35 can deploy it again and the URL is going
- 00:03:37 to stay the same and as you already see
- 00:03:40 I'm using this same syntax used in a
- 00:03:42 normal node Express project because
- 00:03:45 firebase cloud functions for HTTP events
- 00:03:48 use node Express behind the scenes so
- 00:03:51 you can use the same syntax and the same
- 00:03:53 packages for a node express project so
- 00:03:55 that's pretty cool so this is something
- 00:03:57 to keep in mind you can really build
- 00:03:58 your node Express project in a restful
- 00:04:01 way and on a per endpoint basis with
- 00:04:06 cloud functions you can simply write any
- 00:04:09 logic here which you would normally put
- 00:04:11 in one of your routes in the node
- 00:04:12 Express project you might be building so
- 00:04:15 it's wait for this to finish in let's
- 00:04:16 then try this out deployment finished
- 00:04:18 it's worth waiting a couple of
- 00:04:20 milliseconds or half a minute or
- 00:04:22 something like that to really make sure
- 00:04:25 that the function change propagated and
- 00:04:27 you really reach the right endpoint and
- 00:04:29 then if you try again and you send a
- 00:04:31 post request you should get it worked
- 00:04:33 and if you send a get request you get
- 00:04:35 not allowed so now we make sure only
- 00:04:38 post requests trigger this now the idea
- 00:04:40 behind the functions not just to send
- 00:04:43 back some dummy data I actually want to
- 00:04:45 accept some data some form data let's
- 00:04:49 say so a mixture of
- 00:04:50 file and some fields which I can put
- 00:04:53 onto cloud storage for this I first of
- 00:04:56 all want to ensure that course is
- 00:04:57 enabled so cross-site origin resource
- 00:05:00 sharing to make sure that also apps that
- 00:05:03 do not live on the same server as this
- 00:05:05 function will be hosted on so basically
- 00:05:07 all apps but also these apps are able to
- 00:05:10 send HTTP requests to this back-end now
- 00:05:13 for postman this is not important
- 00:05:15 because postman is not a browser it
- 00:05:17 doesn't check for weather course is
- 00:05:19 allowing this access but in a browser if
- 00:05:21 you send some Ajax requests you
- 00:05:23 typically have the issue that the server
- 00:05:25 has to enable course so basically has to
- 00:05:28 allow you to access the resource even
- 00:05:30 though your web app might not be running
- 00:05:32 on the same origin not on the same
- 00:05:34 server so I want to allow that and for
- 00:05:37 that I will install a new package so
- 00:05:39 navigate into the functions folder and
- 00:05:41 with the npm install – – save all in
- 00:05:44 store let's install the course package
- 00:05:46 now this is a node express package we
- 00:05:49 can use to conveniently set some headers
- 00:05:52 we need to send back to the client to
- 00:05:54 allow this crossorigin access and with
- 00:05:57 that installed all imported here course
- 00:06:00 require course this is executed as a
- 00:06:06 function and I pass an object to that
- 00:06:08 function where I set origin to true to
- 00:06:11 setup this these course headers for this
- 00:06:15 origin correctly and then it will
- 00:06:16 actually wrap my entire code here with
- 00:06:19 this course middleware so I will execute
- 00:06:23 course as a function here and this
- 00:06:26 function takes three arguments request
- 00:06:29 response and then the function should
- 00:06:30 execute after it basically added its own
- 00:06:33 headers to the request and response so I
- 00:06:36 will forward request and response here
- 00:06:38 and then execute a function where I also
- 00:06:41 get requests and response and where I
- 00:06:48 then will simply put the code I
- 00:06:50 previously had well in my cloud function
- 00:06:53 itself so now it's wrapped by this
- 00:06:54 course middleware and therefore it sends
- 00:06:57 the right headers to allow crossorigin
- 00:06:58 access since i renamed this in this
- 00:07:01 anonymous function here to request and
- 00:07:04 respond
- 00:07:04 I should all return response status in
- 00:07:07 both cases now to have this work
- 00:07:09 correctly again with that we added
- 00:07:12 course we still don't really accept form
- 00:07:15 data we're not able to do anything with
- 00:07:16 incoming files so that's the next step I
- 00:07:19 want to take and Q accept such file
- 00:07:22 uploads and then store them on fire by
- 00:07:24 storage I need two things I need one
- 00:07:27 package that parses the incoming request
- 00:07:29 body for foreign data and then gives me
- 00:07:32 convenient access to the files and the
- 00:07:33 fields I might have added and I need
- 00:07:35 access to my firebase storage which I
- 00:07:38 already can do or can access with my
- 00:07:40 Google Cloud storage package there so we
- 00:07:43 already got this at least now for
- 00:07:45 parsing that incoming request body I'll
- 00:07:48 use a package name busboys which is a
- 00:07:50 package that is able to handle incoming
- 00:07:52 foreign data so again navigate into that
- 00:07:55 functions folder and run npm install – –
- 00:07:57 save busboys like that and then once
- 00:08:01 this is installed we'll never get back
- 00:08:03 into the main directory and I'll import
- 00:08:06 busboy from the busboy package like this
- 00:08:12 and then I'll go into my course wrapper
- 00:08:15 and after I checked the method off the
- 00:08:17 request I'll create a new busboy
- 00:08:20 instance with new busboy and I'll pass
- 00:08:26 an object to it where I simply forward
- 00:08:28 the headers so set the headers property
- 00:08:31 to request headers does require – well
- 00:08:35 allow bass boy to find out whoever the
- 00:08:37 incoming request is of type form data or
- 00:08:40 not so if it should parse it or not with
- 00:08:43 that I'll use this bass boy object and
- 00:08:46 add the on listener and I'll listen to
- 00:08:49 file events these will be triggered
- 00:08:51 whenever pass boys successfully parses a
- 00:08:54 file from the incoming request in this
- 00:08:57 case we would get an event a method that
- 00:09:01 gets executed a function which receives
- 00:09:04 the field name in which the fall was
- 00:09:07 stored the file itself the name of the
- 00:09:10 file the encoding of the file and the
- 00:09:15 mime type
- 00:09:17 funky gets executed for every foul
- 00:09:19 detected India requests reaching this
- 00:09:22 endpoint now I'm only interested in
- 00:09:25 false if you wanted to also get fields
- 00:09:28 you would have add a busboy on field
- 00:09:33 listener no fields is anything but a
- 00:09:35 file so extra information you add with
- 00:09:37 the request since we're sending foreign
- 00:09:38 data it can be a mixture of a file and
- 00:09:41 extra fields I'm only interested in the
- 00:09:43 file though so once we got a file what's
- 00:09:46 what's the plan
- 00:09:47 the plan is to store it in firebase
- 00:09:49 storage right so what I'll do here is
- 00:09:52 I'll construct a path to the file by
- 00:09:56 using half join which already used in
- 00:10:00 the last video where I get the temporary
- 00:10:02 directory of this cloud function there
- 00:10:07 is a little temporary cloud storage
- 00:10:08 attached to the cloud function it will
- 00:10:10 be cleaned up whenever the execution is
- 00:10:12 done essentially but during the
- 00:10:14 execution you can use it to store files
- 00:10:15 and busboys will store it in some temp
- 00:10:18 directory will store that incoming file
- 00:10:20 there so I'll access the temp directory
- 00:10:22 and get my file there so I'm
- 00:10:26 constructing a path to the file which
- 00:10:27 pass by extract it and then I will
- 00:10:30 create some variable here which will
- 00:10:34 name upload data which initially is now
- 00:10:37 I do this out set of paths boy on
- 00:10:40 instead of pass by on file I'll then set
- 00:10:43 upload data to an object a JavaScript
- 00:10:46 object where I will set my file to
- 00:10:51 default path I created here and where I
- 00:10:54 set the type to the mime type bus boy
- 00:10:57 detected now why do I set it up like
- 00:11:01 this in a variable because now I will
- 00:11:03 leave this path boy on function here and
- 00:11:06 add an a verb or one pass boy on finish
- 00:11:09 bass boy on simply triggers whenever it
- 00:11:12 found a file but finished triggers
- 00:11:14 wanted to stunt parsing the entire
- 00:11:16 request this is when I want to upload
- 00:11:18 the data so here in this handler
- 00:11:21 function and I want to use Google Cloud
- 00:11:23 Storage to upload so I'll create a
- 00:11:25 bucket object here and use GCS bucket to
- 00:11:30 do that to get a reference to
- 00:11:31 a bucket and now I want to get my
- 00:11:33 default bucket for this firebase account
- 00:11:35 you can get this easily by visiting your
- 00:11:38 storage and there it's basically this
- 00:11:42 name without the GS colon slash slash at
- 00:11:45 the beginning this is what I'll pass
- 00:11:47 here this is my default bucket this is
- 00:11:51 the bucket into which I want to store
- 00:11:52 the file now if dead reference created I
- 00:11:55 can trigger bucket upload call that
- 00:11:58 method pass my file and that is why I
- 00:12:01 got this upload data object here where I
- 00:12:04 have a file property which holds a path
- 00:12:06 to the file so now I can pass upload
- 00:12:09 data file here which is that path to the
- 00:12:11 file I want to upload then I can pass a
- 00:12:14 JavaScript object where I will set the
- 00:12:17 upload type to media and set the
- 00:12:23 metadata field Q hold another metadata
- 00:12:27 field where I will set the content type
- 00:12:31 to the upload data type so the content
- 00:12:34 type we originally detected you could
- 00:12:38 add more metadata to the uploaded file
- 00:12:40 of course now at some point of time
- 00:12:42 upload will be done and it even returns
- 00:12:46 us an error or some information about
- 00:12:49 the uploaded file now if we got an error
- 00:12:52 I want to return a response with status
- 00:12:58 code 500 and some JSON object where I
- 00:13:01 essentially pass on the error object but
- 00:13:05 if we don't make it into the safe
- 00:13:06 statement so if we don't have an error
- 00:13:08 then I know that we successfully
- 00:13:10 uploaded the file and then I will take
- 00:13:12 my original response and remove it from
- 00:13:15 the end of that file and put it into
- 00:13:17 this then block here and with that we
- 00:13:22 should have a setup where we can upload
- 00:13:23 a file through HTTP request so let's try
- 00:13:28 it out let's go into the root project
- 00:13:29 folder and run firebase deploy and let's
- 00:13:33 then see if we can successfully send
- 00:13:36 foreign data requests with a file in it
- 00:13:39 cue our back in here and then store that
- 00:13:43 in firebase cloud storage
- 00:13:45 and then it should actually all to
- 00:13:46 trigger our resize function which we
- 00:13:49 wrote in the last video so let's see if
- 00:13:51 that both is the case the deployment is
- 00:13:54 done here again don't rush this it takes
- 00:13:57 some time to propagate that new endpoint
- 00:13:59 to the web so I'll go back to postman
- 00:14:02 and now I will go to body of my post
- 00:14:04 requests make sure to set this to
- 00:14:06 foreign data and then add a file here
- 00:14:10 gif does any name you want like image
- 00:14:13 and choose a file of course I'll take
- 00:14:16 that the winter image we used before now
- 00:14:19 this is the request I want to send so
- 00:14:21 let me hit Send here and I actually get
- 00:14:24 back an error could not handle the
- 00:14:26 request let's have a look at our
- 00:14:27 firebase functions log here so on
- 00:14:31 firebase cloud functions I'll have a
- 00:14:33 look at my upload file log here and
- 00:14:37 there I can treat headers of undefined
- 00:14:40 at course hmm
- 00:14:43 yeah because I got an error here the
- 00:14:45 course middleware does execute a
- 00:14:48 function here but actually the original
- 00:14:50 request and response object are just
- 00:14:53 edited so that is my mistake here also I
- 00:14:56 never changed this we're just correct
- 00:14:58 because I should keep the original
- 00:14:59 request not renamed us to requests just
- 00:15:02 keep a direct we're here and the same
- 00:15:04 for a response use response arrests and
- 00:15:08 not the response we used before
- 00:15:09 everywhere because course just appends
- 00:15:20 to this original data it does replace
- 00:15:22 this so this is my mistake sorry for
- 00:15:24 that so let's quickly rebuild deploy
- 00:15:25 this and then test us again so now the
- 00:15:28 updated function redeployed let me try
- 00:15:30 this again
- 00:15:31 and again let's not rush this it can
- 00:15:33 take a while so let's try sending this
- 00:15:36 again and now it's taking really really
- 00:15:39 long here then you can see in the lock
- 00:15:43 that it started a function execution but
- 00:15:45 somehow it's not finishing here now it
- 00:15:48 will timeout after 60 seconds that's the
- 00:15:52 default timeout so you got no infinite
- 00:15:54 running function costing you thousands
- 00:15:57 of dollars by
- 00:15:58 way the basic execution so a couple of
- 00:16:02 executions which are clearly defined in
- 00:16:04 the official Docs actually with more
- 00:16:07 than just a couple are free so you can
- 00:16:10 run your function quite a lot without
- 00:16:11 paying anything only if you exceed that
- 00:16:14 limit you will have to pay but again you
- 00:16:16 can find this in the official Docs and
- 00:16:18 there on the pricing pages it is
- 00:16:20 described how often you can run your
- 00:16:22 function in the free tier and from which
- 00:16:24 point on it will cost you money so here
- 00:16:27 you can see it timed out after this one
- 00:16:30 minute the question of course is why did
- 00:16:33 it timeout what what's wrong here why
- 00:16:35 doesn't this work it actually has two
- 00:16:39 issues our function one is here when we
- 00:16:41 parse file requests when we get incoming
- 00:16:45 files there we get the file path but
- 00:16:49 actually this is not a file path that's
- 00:16:52 why we'll automatically store the fallen
- 00:16:54 itself all puffs we can use to store the
- 00:16:57 fallen so first of all let's import and
- 00:17:00 now we're dependency the file system
- 00:17:03 package which is a default node package
- 00:17:05 so we don't need to install it
- 00:17:06 separately and then in the file listener
- 00:17:10 here I will use that file busboys
- 00:17:13 detected and add the pipe method to do
- 00:17:16 something to that file and then I will
- 00:17:18 use FS create right stream you basically
- 00:17:22 stream the file content into a new file
- 00:17:25 and pass file path as an argument this
- 00:17:28 will actually write this file to the
- 00:17:29 system now that's one thing another
- 00:17:32 thing is that Google Cloud Storage
- 00:17:35 whilst we could use it in our cloud
- 00:17:38 storage trigger in the HVP trigger it's
- 00:17:43 not available it's not initialized we
- 00:17:46 need to initialize it manually with the
- 00:17:48 credentials of our firebase project that
- 00:17:51 we can do it as easily by going to our
- 00:17:52 firebase project and then to the
- 00:17:54 settings project settings and there to
- 00:17:57 service accounts there you can now
- 00:17:59 download under firebase sta admin SDK
- 00:18:02 you can download a private key which you
- 00:18:05 need to initialize cloud storage so you
- 00:18:07 can click generate new private key
- 00:18:11 and then stored it in your project
- 00:18:13 folder so here in the functions folder
- 00:18:17 so now I added this file which holds my
- 00:18:20 private key and now in your firebase
- 00:18:23 cloud function you actually need to
- 00:18:26 initialize your Google Cloud storage a
- 00:18:29 bit differently
- 00:18:29 you need to initialize it such that you
- 00:18:31 can use it in every function so I'll put
- 00:18:33 it a bit further down because before we
- 00:18:36 import it I'll add a new constant which
- 00:18:39 we'll name GC config for a Google Cloud
- 00:18:41 config where I will just specify the
- 00:18:44 project ID woops like that make sure the
- 00:18:48 casing is correct project ID which is
- 00:18:51 just your firebase project ID I'll add
- 00:18:54 it in a second and a key file name
- 00:18:56 property which is just the name of that
- 00:18:59 JSON file make sure to add dot J's and
- 00:19:02 you just generated and download it and
- 00:19:04 store it in your project so the project
- 00:19:06 ID is something you can find in the
- 00:19:08 firebase RC file for example so I'll add
- 00:19:11 this here also as a string and this
- 00:19:15 Google Cloud Connect has to be passed to
- 00:19:19 that function we execute here when we
- 00:19:20 import the Google Cloud Storage package
- 00:19:22 I'll just pass this GC config object to
- 00:19:25 it to initialize that and with that I
- 00:19:28 now make sure that Google Cloud Storage
- 00:19:31 all the works in this HTTP trigger
- 00:19:33 function and not just in the storage
- 00:19:35 related cloud function that is something
- 00:19:37 you have to keep in mind if you want to
- 00:19:39 use the Cloud Storage package in a non
- 00:19:42 storage related trigger function then
- 00:19:44 you have to initialize it with your
- 00:19:46 private key you download it from the
- 00:19:48 firebase console now if then we're
- 00:19:50 almost there but there's one more thing
- 00:19:52 we have to do to kick off this whole
- 00:19:55 busboys thing after all our on listeners
- 00:19:59 and so on I also have to call Busboys
- 00:20:02 and and pass the request raw body Hewitt
- 00:20:07 raw bodies a property provided to us by
- 00:20:09 a cloud function itself so to say on the
- 00:20:12 request object and this simply well is
- 00:20:16 required for Busboys to work correctly
- 00:20:18 and to start parsing or to to parse all
- 00:20:21 the requests and trigger
- 00:20:25 on events now with that we're almost
- 00:20:27 there one more thing though here in the
- 00:20:31 upload then block that's of course a
- 00:20:32 mistake here we just get a function here
- 00:20:36 if we are successful and if we would
- 00:20:39 have an error then we would actually end
- 00:20:42 up here in the catch block so that is
- 00:20:45 where we then received the error then we
- 00:20:47 then in turn want to handle we don't
- 00:20:49 need an if step in here we would just
- 00:20:51 return our error response here so just
- 00:20:55 like this this is the adjustment need to
- 00:20:58 make this syntax where you get the error
- 00:21:02 or the uploaded file reference which you
- 00:21:04 also could use here that would only be
- 00:21:06 relevant if you were to pass you see an
- 00:21:09 alternative way of using the upload
- 00:21:10 function by passing a callback function
- 00:21:12 where you then indeed have your error or
- 00:21:15 the uploaded file but if you use the
- 00:21:17 promised approach as I do you work with
- 00:21:19 then and catch of course so sorry about
- 00:21:21 that and with that let's deploy this
- 00:21:24 firebase function and let's see if with
- 00:21:26 all these changes it works as it should
- 00:21:28 now let's already do something else
- 00:21:30 let's go to storage and I already got
- 00:21:33 some files in there which actually are
- 00:21:34 defaults I will upload so I will delete
- 00:21:36 them to make sure that we really can see
- 00:21:38 if that works so that I got an empty
- 00:21:41 storage now if that I'll wait for this
- 00:21:43 to finish and then we'll try out sending
- 00:21:46 the same act request again and hopefully
- 00:21:48 with that storing a file on cloud
- 00:21:50 storage through our firebase HTTP
- 00:21:54 triggered cloud function pretty advanced
- 00:21:56 I guess deployment finished and as
- 00:21:59 always let's give this a few seconds
- 00:22:01 before we send the request and then in
- 00:22:04 postman all sent the same request again
- 00:22:07 queue firebase and I get back it worked
- 00:22:11 which is looking good
- 00:22:13 now on storage I'll reload this and
- 00:22:16 actually there we should see two files
- 00:22:18 now we see the uploaded file and the
- 00:22:22 resized one because our other cloud
- 00:22:24 function which triggers upon file
- 00:22:26 changes and of course this is a file
- 00:22:28 change it's uploaded so this triggers
- 00:22:30 the cloud function
- 00:22:31 the average cloud function also runs so
- 00:22:34 the resizing cloud functions all the
- 00:22:35 runs and here I can confer and by the
- 00:22:37 date and time
- 00:22:38 this is today so this works as it should
- 00:22:41 and with dad with some errors we made it
- 00:22:45 to the end of that we have our upload
- 00:22:48 file HTTP endpoint which is triggered by
- 00:22:51 an incoming request we check the request
- 00:22:53 method we make sure that we would all to
- 00:22:56 be able to reach this from some web app
- 00:22:59 which uses a browser which therefore
- 00:23:01 make sure that course is enabled we do
- 00:23:04 enable it here we use bass voice you
- 00:23:07 parse the foreign data and parse any
- 00:23:10 files store them in a temporary folder
- 00:23:12 and then once we parse the hold request
- 00:23:15 upload it through the initialize Google
- 00:23:18 Cloud Storage package and then we will
- 00:23:21 kick off this whole parsing essentially
- 00:23:23 here by calling end and saying we're
- 00:23:26 done reading in the request here's the
- 00:23:29 raw body please do something with it
- 00:23:31 this is what's happening here and they
- 00:23:33 hope that despite the few errors we
- 00:23:36 faced which also might have been helpful
- 00:23:37 seeing some things to avoid you learned
- 00:23:40 how to set up HTTP endpoints through
- 00:23:44 firebase cloud functions and which extra
- 00:23:47 flexibility just gives you now you can
- 00:23:49 build your own restful api on top of
- 00:23:52 firebase you can use all the other
- 00:23:54 features you can integrate this API with
- 00:23:57 the other features like we do for
- 00:23:59 storage and you can add extra
- 00:24:01 functionality that previously you
- 00:24:03 couldn't add easily at least