Hooking A Web Page To Firebase With VueJS

In the the last post in this series I set up a bunch of functions that go off in response to a set of events. This is dandy, but how will the client know when the transaction is completed? What does “completed” even mean?

Defining Done

At a high level, here are the things that we need to do when an order comes in:

  • capture the customer’s money
  • create a sales order, which is our record of sale
  • fulfill the order (digital downloads)
  • create an invoice which is the client’s record of sale
  • email the customer with links to their downloads and the invoice
  • create reporting entries somewhere
  • optionally notify the store owner

There could be more or less than this, but I think this is a good start.

To a store owner, “done” might be when the customer is notified and they download the goods; at that point the order status might be sent to “closed” or something.

To the customer, “done” means I paid now gimmeh. In an old school ecommerce site things are typically synchronous, so there’s no joy for the customer until all things are done. If there’s an error along the way, support is going to get a call and we’re not going to look very good.

In an evented/asynchronous/realtime system we can do better.

Charting The Progress

If you recall, every function that we’re firing is updating a progress record on our sale:

exports.stripe_charge = functions.https.onRequest((req, res) => {  
  //captures the charge
  //updates as sale record with a transaction:
  //sales/{id}/transaction
  //update progress
});

That record is a literal one, as you see here:

This is the key: if our functions keep updating this progress field, we’ll be able to listen to it and react realtime on the client. We can then decide when the order is “done” from the customer’s perspective.

Hooking Up VueJS

Time to jump back over to our static site, which I’m building out with middleman. Again: you can use whatever static app you like, or none at all! To me this is one of the best aspects of working with Firebase (the entire suite): I’m not tied to a single framework to do everything. I can build my website however I damnwell please.

So let’s plug in VueJS along with a plugin for Firebase called VueFire:

  <!-- Vue -->
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <!-- VueFire -->
  <script src="https://unpkg.com/vuefire/dist/vuefire.js"></script>

Initializing Firebase

The next step is to initialize Firebase from the client. It’s triple-important to remember that this is the client SDK, not the admin one we’ve been using. All of the rules will be applied that we’ve created previously.

To get a quick script for our setup, we can go to the Firebase console for our app, click “Authentication” in the nav menu, and then “WEB SETUP” which is in the top right. This will pop the code you need:

Put that at the top of the page.

Initializing The App

I already made a checkout page that I like, and you can see it here. The easiest thing is to view source on the page if you want to see it all. The first thing to notice is this bit of markup at the very top:

<div id="order" class="ui container">
  <div class="ui horizontal divider">
    <h2 class="ui header">Your Order</h2>
  </div>

I’m wrapping everything in a div tag with the id of order. I’m doing this so I can initialize VueJS thus:

<script>
Vue.use(VueFire);
var db = firebase.database();
var app = new Vue({
  el: "#order"
});
</script>

Like Angular, Vue will now treat this div as a template wrapper. In addition I’ve hooked up VueFire and created a reference to the root of my Firebase database.

Listening To The Order Progress

Now we get to the good stuff! If you recall, I’m creating the order’s ID on the client. I could use a GUID for this, but I decided to do something a bit more meaningful using MomentJS and a random string generator:

function shortid(length) {
  return Math.random().toString(36).substring(2, 4) + Math.random().toString(36).substring(2, 4);
};
generateId : function(){
  const formattedDate = moment().format('YYYYMMDD-HHmm');
  const key = shortid(4);
  const id =  `RED4-${formattedDate}-${key}`;
  this.orderId = id;
}

This will give me an ID like RED4-20170520-1016-zgb6 that is a bit more meaningful. At a glance I can tell which store this order is from and the date/time. There’s also a good bit of entropy at the end there so order numbers aren’t guessable. I suppose it could be better, but I like this.

The important thing is that I know the order number on the client. Doing this will allow me to listen to the progress of that order, even though it doesn’t exist yet in the database. To do so, I have to tell VueFire what to listen to:

var app = new Vue({
  el: "#order",
  firebase : {
    progress: {
      source: db.ref(`sales/${Cart.orderId}/progress`),
      asObject: true
    }
  }
}

Before this will work, however, we have to make sure we’ve set the rules so we can listen:

Firebase rules aren’t the easiest thing to get used to, but once you write a few of them they get easier to understand. Here I’m saying “for every sale, the progress field can be read but not written to”.

Now comes the fun part!

A Realtime Checkout UI

There are probably better ways to do this! I’m not a CSS expert nor am I graphically inclined; so feel free to have some fun! I decided to use Semantic UI, specifically the steps bits to show what’s happening when.

There are four total steps (order received, payment received, order fulfilled, emailed) that I want to show to the customer and I can do that by changing the CSS according to what’s happening with the progress field at Firebase. Here’s the HTML/VueJS snippet for doing that:

<div v-bind:class="{completed: progress.captured, active: true, step: true}">
  <i class="hand peace icon"></i>
  <div class="content">
    <div class="title">Payment Received</div>
    <div class="description">Processing...</div>
  </div>
</div>

I won’t go too deeply into VueJS in this post, just know that that it’s toggling the completed class based on whether progress.captured is true. VueJS knows what progress is because we bound it above, in our app initialization.

The really nice part about all of this is that we can show a download button once the order is fulfilled; the customer doesn’t have to wait for the email to be sent:
hooked up to Stripe, but no emails will go through (I disabled that part).

<div v-if="progress.invoiced" class="ui container" style="margin-top:52px">
  <h3 class="ui centered header">Order Number: {{orderId}}</h3>
  <p>Thank you for your order! The downloads are below, please note that they are limited. We also ask you
  kindly to not share.
  You will receive an email shortly with your download information. Please hang on to it as it is your record of sale.
  </p>
</div>

Here’s a checkout that’s complete, as far as the client is concerned, but has not yet completed as far as we’re concerned (no email sent or reporting created):

Wiring Serverless Routines With Firebase

In the previous post we sent Stripe Checkout information to an HTTPS-triggered function – basically an API endpoint. Now we need to execute the charge and we can do that with Database-triggered functions.

Evented Functions: What Goes Where?

One of the most underutilized tools of Node, in my opinion, is the EventEmitter. There’s a lot of good stuff you can do with the thing, but to use it you need to shift the way you typically write code. In short: you don’t orchestrate logic, you respond to events. Orchestration is a side effect, in a way.

The same is true with Firebase Functions. Our goal is to have small, concise little functions that do a thing based on some criteria. For instance:

  • The stripe_capture function might get triggered by a write to the sales path
  • A fulfill_order function would be triggered when a transaction is written
  • A notify_customer function might get triggered when an invoice is written

This all makes sense, logically, but is it the “right thing to do”? Theory often clashes with reality, so let’s have a think.

Invocations and Timing

You don’t want your customer to wait while your routines are queued and triggered. If we divide up everything we need to do (capture the charge, generate invoices, rights to downloads, account creation, notification, etc) into little functions, each of those will need to fire in order to complete the order. Are those invocations instant?

Probably not. There is a triggering mechanism that works from a queue, the more we involve this mechanism (as fast as it is), the longer things might take. From my experience, the invocations happen rather quickly but, as I mention a few posts ago: Akamai. Let’s not introduce a possible problem if we can avoid it.

What Needs To Happen When?

One of the problems with doing event-based programming is that you often need to do things in a serial fashion. For instance: you don’t want to send an email to a customer before their invoice (and fulfillment) is generated; that would introduce a race condition.

If we divide everything into “micro routines” then we’ll need to think a lot about what happens when and where. This begs the question: why are we doing event-based stuff in the first place?

This line of thinking opens the door to a couple of options we should consider fully.

Option 1: Everything Needs To Happen Now

Some businesses consider the entire sale to be a transaction. From the moment you’re handed the money to the point where you notify the customer and write the reporting entry – it all needs to happen inside of a transaction. If any of it fails, it all needs to fail.

If this is the case, then one function with ordered steps is what we need. The minute money comes in the door we do the things we need to do, in order, and we’re done. We’ll need a rollback mechanism of some kind (which could be a simple delete command) which we could use a simple try/catch block for:

exports.sale = functions.database.ref("sales/{id}/checkout").onWrite(ev => {
  return co(function*(){
    //capture the charge
    try{
      const transaction = yield stripe.charges.create(...);

      //generate the invoice

      //fulfill the order

      //notify the customer

      //save to reporting

      //update the sale record and close the order

      return {success: true} //whatever you need
    }catch(err){
      //rollback everything
      return {success: false, error: err};
    }
  });
});

I’m using co with generator functions to orchestrate the serial stuff, but you could use whatever tool you like (such as async). With co, you can use a try/catch block to handle async errors, which is what we’re doing here.

This works and has the benefit of being fast – but it also means that you can’t gracefully recover. Any error in the chain here will cause the sale to fail, which to me is a really bad idea.

Option 2: Synchronous Little Chunks

Errors happen and I think it’s better to build a system that let’s you recover if there’s a problem. For instance: the customer might have accidentally entered an invalid email. Let them know that right at sale time so they can fix it!

Maybe they entered there name as 👻 and your database isn’t setup to handle that kind of string encoding – does that mean you should lose a sale? No way! Fix the problem on your end and resume the sale.

But what are these little chunky functions supposed to be? For me, I have a rule: take the money and run… the rest of the functions :). Here are my functions:

exports.stripe_charge = functions.https.onRequest((req, res) => {
  //captures the charge
  //updates as sale record with a transaction:
  //sales/{id}/transaction
  //update progress
});
exports.fulfill_order = functions.database.ref("sales/{id}/transaction").onWrite(ev => {
  //create deliverables
  //set the access rights
  //create an invoice and write it to /sales/{id}/invoice
  //update progress
});
exports.notify_customer = functions.database.ref("sales/{id}/invoice").onWrite(ev => {
  //email the customer their invoice and a link to downloads
  //update progress, close order
});
exports.update_reporting = functions.database.ref("sales/{id}/invoice").onWrite(ev => {
  //email the customer their invoice and a link to downloads
  //update progress
});

A number of things are going on here, so let’s step through it.

First, I’m making sure that the sale gets recorded when the transaction is captured by Stripe. This is something you don’t want to forget about :). When the transaction record is captured I write it to the sales/{id}/transaction path in Firebase. Doing this triggers the next function: fulfillment.

The fulfillment function does a lot of stuff. This might rub a few of you the wrong way as it violates SOLID, but I really don’t care :). Divide it out into smaller functions if you like, I prefer simplicity. When this function completes, I write the invoice to the sales/{id} path, which triggers the final two functions.

At this point the order is complete, as far as the customer is concerned. They’ve paid us, we’ve generated their invoice – let’s not make them wait until we send off an email and create a reporting entry.

But how do we do this? All of these functions are happening “in the background” if you will; how do we let the customer know what’s happening?

Firebase is a realtime database. The client SDK can listen to any changes in the data at any path! We know the order id because we generated it on the client – this means that we can listen to the progress of the order, and when the deliveries are ready we can let our client access them directly. Even if the order hasn’t finished processing.

I’ll tackle that next time.


See this series as a video

Watch how I built a serverless ecommerce site using Firebase. Over 3 hours of tightly-edited video, getting into the weeds with Firebase. We’ll use the realtime database, storage, auth, and yes, functions. I’ll also integrate Drip for user management. I detest foo/bar/hello-world demos; I want to see what’s really possible. That’s what this video is.

Thinking In Events With Firebase

Image credit: CCPixs.com

The first two posts in this series were philosophical musings on “why serverless” and “why not serverless”. With this post let’s write some code.

Rules…?

I was talking with Matthias Brandewinder recently at NDC Oslo. He does a lot of serverless stuff, but mostly with Azure Functions. One of the things we discussed was the absence of “patterns” for how to build things in a serverless environment.

Not the Gang of Four stuff – we were talking about architectural patterns. The question was a simple one:

Do we build things like we would otherwise? Or is there some type of pattern out there yet to be discovered?

I don’t know – and that’s kind of fun! I’ll ponder this question repeatedly as I add posts to this series. For now, let’s take the first step on our journey…

The First Function

I’m building an ecommerce app, nothing terribly surprising about how it works. Product pages, a cart of some kind and finally a checkout page powered by Stripe:

This here is Stripe Checkout. You could write your own checkout form if you want, but I dig this.

Stripe works by sending the raw credit card/address information directly to Stripe, bypassing my app entirely. What I get back is a “token”, which is a reference to the checkout information stored on Stripe’s servers. I don’t want to go too deeply into all of this right now – have a look at their docs if you’re confused.

Once I have the token, I need to execute the charge. I’m not going to do this on the client, obviously, which makes this scenario perfect for Firebase functions.

To AJAX or Not?

Here is my Stripe configuration code:

var handler = StripeCheckout.configure({
  key: 'MY PUBLIC KEY',
  image: 'https://app.redfour.io/images/icon/apple-icon-180x180.png',
  locale: 'auto',
  zipCode: true,
  billingAddress: true,
  token: function(token) {
    var payload = {
      order: {
        id: Cart.orderId, //RED4-20170622-k4kdls
        processor: "stripe",
        items: Cart.items
      },
      payment: token
    };
    //now what?
  }
});

The token callback is the thing we’re most interested in. This is the code that fires once Stripe has processed and stored the user’s checkout information. Here I’m creating a payload, which has order information (including an order id, which is very important) and the order items. Finally I’m attaching the token, as that’s what we’ll use to capture the charge.

The question is: how do I get this to Firebase?

Option 1: Writing to the database

We know that Firebase functions are triggered off of events. The most logical one at this point is to use an “https event”, which is triggered when a specific URL is called. I could do that, or I could lean on Firebase completely by simply writing this payload directly:

firebase.database().ref(`sales/${Cart.orderId}/checkout`).set(payload);

This is using the browser-based firebase SDK to write my payload to the path specified. Notice how I’m using the Cart.orderId here? This serves two purposes:

  • I can use it to define a path to a sale in the database
  • I can use it to listen to a path to a sale in the database

This is where thinking in events starts to take shape! By writing this checkout information, we’ll be able to trigger a cascade of events that will, hopefully, culminate in a sale.

Problem: Security… Hello?

If you’re wondering if I’ve fallen and hit my head – I don’t blame you. Allowing the public to write to your database is completely lame! The good news is that we have some rules we can define to help us out. Let’s take a look at them now and I’ll explain more as we go:

Every firebase database allows you to specify a set of rules for working with data. These rules dictate what can be written, read, how to validate the data and finally how it should be indexed. I’ll get to all of this later on, for now, focus on the highlighted area.

I’m specifying rules for a given path using a JSON structure. This isn’t the easiest thing to get used to, but it only takes a few Googles to understand it. Here I’m specifying the path to the checkout data should be governed by a few conditions:

"sales" : {
  ".read": false,
  ".write": false,
  "$sale" :{
    "checkout" : {
        "payment" : {
          ".read" : true,
          ".write" : "newData.exists() && !data.exists()",
          ".validate" : "newData.child('token').exists() && newData.child('email').exists()"
        }
    },
}

The first thing to notice is that rules cascade. At the very top I’m specifying that the public can’t read or write to the sales path. I’m overriding that on a per-sale basis by using $sale, which is a placeholder for every child of the parent sales key.

I override the parent read/write settings at the checkout/payment path, allowing reads, but only allowing writes when there’s new data and a record doesn’t already exist (newData and data are predefined variables). This will prevent people from changing their payment information.

Next, I have some simple validation, which ensures a token and email exist. By the way: if you’re wondering what the difference is between .validate and .write (as they basically do the same thing) – .validate does not cascade, which .write does.

Is this enough? Would your serverside code do more validation than this? You can build a pretty extensive validation expression here – it’s just JavaScript that gets eval’d every time a write happens.

Does It Matter?

Imagine you have a new CTO. Or maybe your company was just bought or transferred to a new division. You’re sitting in a code review and this new CTO (or tech manager, whatever) looks at your code and asks:

Let me get this straight… you’re letting the public write directly to the database?

Imagine if this meeting was taking place after an incident of some kind. It wouldn’t even need to be related to the checkout process. Heads will need to roll, and there’s very little you can say to get yourself out of this kind of jam.

Technically speaking, Firebase’s rules are pretty solid and you can do a number of things to mitigate bad guys. Politically speaking you might be putting a noose around your neck.

Your call.

Option 2: Make an AJAX Call

The easiest thing to do here is to just make an AJAX call:

var checkoutUrl = "https://some_firebase_function_url.com/stripe_charge";
$.ajax({ 
    type: 'POST', 
    url: checkoutUrl, 
    data: payload,
    dataType: 'json'
}).done(res => {
  console.log(res)
}).fail(err => {
  console.log(err);
});

This function will receive the payload, write it to the database and kick off the same cascade of events that you would otherwise. There are some other advantages as well:

  • You can examine an IP address and stem flooding
  • You can use anonymous authentication to do the same (which I’ll talk more about in a later post)
  • You can write more comprehensive validation code

The downside to this is that these validations are in code at the app level, not nestled happily in front of the data. The best choice, probably, is to do a combination of the two things:

  • Keep the validations discussed in Option 1, above
  • Protect flooding using Option 2

Like I mention up top: there are no patterns here yet. This is all sort of new-ish, and this answer seems good to me.

Now What?

Now we get to write our receiver function – the entry point to our serverless backend. We’ll use an https triggered function to write the new order, and a further set of functions to process it both serially and concurrently.

We’ll do that in the next post.


See this series as a video

Watch how I built a serverless ecommerce site using Firebase. Over 3 hours of tightly-edited video, getting into the weeds with Firebase. We’ll use the realtime database, storage, auth, and yes, functions. I’ll also integrate Drip for user management. I detest foo/bar/hello-world demos; I want to see what’s really possible. That’s what this video is.

Should I Trust Firebase? Of Course Not!

In the last post I discussed my initial foray into the serverless thing, and why going with other platforms (AWS Lambda, Webtask.io, etc) didn’t make sense at the time.

I’ve received a number of comments since then, specifically about this post which details how one company had their costs jacked up by Firebase 7000%:

Due to a change in how they report data usage, our monthly costs for Firebase, a SaaS provided by Google, has increased from $25 a month to what is now moving towards the $2,000 mark — with no changes to our actual data use. This change was made without warning.

The question was a simple one: how can I possibly trust my business to a company that would do this sort of thing? My answer, in most cases is simple: you shouldn’t. Not now, not ever.

Let’s dive into this.

A Quick Summary Of This (and Every Other) Service Dissapointment

There are a number of problems discussed in the post referenced above, but they basically distill thus:

  • The startup (HomeAutomation) was doing IoT stuff and had a query going off once a minute from all of their distributed devices. The query was small (a flag check) and when the company checked their bandwidth usage they were within the limits of their $25/month plan which let them push 20 gigs of data per month (which is a hell of a lot).
  • Firebase realized they had a bug in their bandwidth measurements: they weren’t accounting for the SSL payload. They fixed this in March/April of 2017. This fix caused HomeAutomation’s bill to go up by 7000%.
  • HomeAutomation could not fix this problem without going offline and changing their app entirely. Given that it’s IoT, this basically means going out of business.
  • Firebase was not responsive and it took a blog post to push them to (rightly) credit costs that were not foreseeable nor accountable. They have corrected it, but the customer service hit here is really, really bad.

No one looks good in this. Yes, Firebase did something dumb. I’ve been trying to find the right words to express my thoughts correctly, and I think I’ve hit on one:

Duh

Firebase (and therefore Google) are Big Companies. They’re in this to make money and if you’re getting by on $25/month for a large distributed IoT system then yes, something is probably wrong.

I know that it sounds like I’m about to blame the developer for trying to plan for something that they had no actual metric to plan for it with. This is true, Firebase wasn’t telling them accurate information, but as they say in Hawaii…

Akamai!

I don’t trust many people. I don’t trust any businesses either. One company I used promised “unlimited bandwidth” until I soaked up 4Tb in a single month and they tried to charge me, saying “it was unreasonable for you to do that”. Sure, whatever, lates.

I’m ready to jump ship with any service/platform on a moment’s notice. I’m almost too ready as a matter of fact and routinely need to get pulled off the ceiling by peers. I’m SaaS trigger happy I guess.

I could fill this entire post with the different companies that have let me down over the last 2 years, doing shady things and trying to overcharge me. Other’s have simply disappeared without telling people. While fun (for me), I don’t want to derail this post too much.

Let me just summarize this way: if something seems just too good to be true, expect a nasty surprise.

Surprise!

Paying $25/month pushing 10s of gigs of data over the wire fits that description.

Coding For The Future

Pardon me while I climb the stairs to my Ivory Tower… ahh the view is nice from here! I can see the past in perfect 20/20 clarity…

When you write software applications it’s usually a good idea to have a look at what other people have done before you. This situation could have been mitigated/avoided by embracing a simple sentiment that my friend Rob Sullivan stated quite elegantly once in a talk he gave at NDC Oslo:

Change hurts when you write bad code, doesn’t it?

Developers in the previous decades have learned the hard way that hitching your wagon to any particular dependency, and betting your entire business on it will always be a bad thing. This goes for services, frameworks, and even the people themselves.

HomeAutomation seems to have just found this out, as this was one of their summary points:

Always build your architecture in a way that will avoid becoming trapped into a specific service. Build your application in a way that swapping one service for another is as simple as possible.

You have come correct sir. Writing apps is indeed hard and takes some forethought.

So, Should You Trust Firebase?

No. Don’t “trust” anything. As a developer you’re not allowed to “trust” any language, service, platform or other coder for that matter. Most of all: you’re not even allowed to “trust” yourself. In short trust has nothing to do with creating software.

Firebase is a service that you can use with a reasonable amount of confidence. It can help you get your app out quickly and, hopefully, if you need to change something later on then you should be able to do that!

I know, I know: Ivory Tower Dev wagging Ivory Finger. But hoooonnnneeessstlllyyyyyyyyyy do we have to keep learning this lesson again and again?

At the very least – if your app is pushing 10s of gigs of data per day, don’t you think it’s reasonable to ask the question:

“Are you sure we only have to pay $25/month”

This would scare me. It should scare you too! Here’s the really scary part: even if they built their app to handle a complete service change, they would have been in serious trouble anyway.

Let’s say they made it easy to move their system to AWS, for instance. They’re in business to make money too, as it turns out, and if you’re pushing 10’s of gigs of data per day (or in HomeAutomation’s case, 100s of gigs of data though they didn’t know it at the time) you will have to pay for that. $2000/month is not unheard of for this.

I am going to be extremely curious what happens to this company in the next few months. No excuses for Firebase here either… this was a complete mess on all sides.

Which means you and I have to build defensive systems with interchangeable parts… because change sucks no matter what you do.

I’ve been using Firebase for 3 years total now, for very small things compared to HomeAutomation. I like what I’ve seen and so far I’m happy. This doesn’t mean you will be, nor does it mean that I will be in a year or so.

I’m expecting that. You should be too.


See this series as a video

Watch how I built a serverless ecommerce site using Firebase. Over 3 hours of tightly-edited video, getting into the weeds with Firebase. We’ll use the realtime database, storage, auth, and yes, functions. I’ll also integrate Drip for user management. I detest foo/bar/hello-world demos; I want to see what’s really possible. That’s what this video is.

Kicking The Tires On This Serverless Thing

I just released a video series about building a serverless application with Firebase, and I thought I would write it up in a blog series as well. I think it’s worth reading and understanding what I went through. This is Part 1 of that series.

The Punchline

Before doing this I spent a few months with AWS Lambda (which you’ll read about below); first with The Serverless Framework (which I quite like – so jealous they got that domain!) and then with ClaudiaJS which didn’t quite fit what I wanted. Finally I rolled my own thing with some shell scripts and a zip file.

I ended up with a mess that cost more money than I wanted it to. It wasn’t easy to figure out and more than once I had to rage-swim at the Y to get over the stress of the day.

I had better luck with Firebase. I had a fun time and built something interesting. At least I think it is. I had to approach the development process in a completely different way than what I was used to… but I like that kind of thing. I know others don’t. The big thing to me, however, is that I was able to build something that I would truly use. In fact I’m using parts of it now in production (more on that in a future post).

This is a long story and will cover many posts. Not sure how many but I’ll try to keep it focused. As you can clearly tell I like Firebase, a lot, and yes I encountered quite a few issues along the way but they were surmountable. I’ll get into all that, I promise.

For now let’s start at the beginning, when I first dug into the “serverless” thing.

AWS, Firebase, Webtask.io…?

Let’s start with Webtask. I know a number of people over at Auth0 (who own and run Webtask.io) and I have a ton of respect for them. Everything you’re about to read has been said to them in person, so don’t think I’m being unfair – I think they would agree with me straight away.

If you head over to the Webtask site you’ll see a simple page with a headline:

It’s a puzzling headline with a rather sparse lede. If you click on anything on this page (the green button or “learn more”) you’re asked to log in. Once you log in you’re taken to this page:

It’s gorgeous, as you can see, but it just adds to the confusion. What “more” am I supposed to learn here? How much does this cost? What language do I use? What… is happening?

I’ve built a few tasks with this tool and they worked great, but from there… I have no idea. If you read their pricing you’ll become more confused, most likely. The documentation looks promising but is, once again, more than a bit sparse.

Verdict: Webtask is a neat idea but doesn’t seem to be the thing I need. In fact I have no idea what it’s supposed to be.

AWS

Amazon introduced AWS Lambda over a year ago and it made a big splash. The pitch was simple to understand:

AWS Lambda lets you run code without provisioning or managing servers. You pay only for the compute time you consume – there is no charge when your code is not running. With Lambda, you can run code for virtually any type of application or backend service – all with zero administration. Just upload your code and Lambda takes care of everything required to run and scale your code with high availability. You can set up your code to automatically trigger from other AWS services or call it directly from any web or mobile app.

If you’ve worked with AWS before, you know that nothing AWS is ever simple. I went into this with a good dose of skepticism!

Step 1: Pick Your Function

After signing into the AWS console and clicking on “Lambda”, you’re sent to a splash screen with a button that says “Get Started”. You’re then taken here:

Welcome to AWS. It might just be me, but I feel like working with AWS is like wandering around the Mall of America, hoping to find that one store that sells that one thing you’re trying to find.

Note: for foreign readers, the Mall of America is a gigantic structure in Minnesota that embodies everything that is not wonderful about the US.

AWS is conceptually deafening. I am literally exhausted after working with it for a few hours and often I roll out of my chair onto the floor, weeping, as I can’t remember what I just did nor how much it will cost me.

OK maybe a little hyperbolic. There’s just a lot of moving parts is what I’m trying to say. So, where was I …

Step 2: Pick Your Trigger

Right: after you pick you function (good luck with that) you’re given this screen:

Your function is executed based on some type of event. If you know AWS and you know what these services are, you’re in luck! If not, say goodbye to the next few hours.

Step 3: Get Lost In The AWS Wilderness

We want to choose “API Gateway” for this so we can call our function from the outside world over HTTPS. And then…

This is where I’m going to hit the fast-forward button. I remember swearing a lot and feeling lost at this point (not hyperbole). I mean: I know what these things are, but I don’t know the implications fully. We’re talking about security here, and staging environments! These aren’t to be taken lightly.

I tried to figure it out myself for 2 solid days, and then I just gave up and went with a framework.

Serverless Framework

The Serverless Framework is a Node project that automates most of the pain when dealing with AWS. The video on the site is reasonably informative but… clicking on the documentation link (from the home page) gives you a 404, which I think is funny. If you use the menu on the top you’ll be OK.

So, in short, you use their CLI to generate a YML file and a Node file for your function code. You can use Python or Java with Lambda, but for now we’ll stick with Node.

As with most HelloWorld demos, the initial steps look simple. I’m not interested in that, I want to see what complex looks like. Here ya go:

service: bigmachine-checkout

provider:
  name: aws
  runtime: nodejs4.3
  region: us-west-2
  stage: dev
  vpc:
    securityGroupIds:
      - "sg-derptyderp"
    subnetIds:
      - "subnet-herptyherp"
  iamRoleStatements: # permissions for all of your functions can be set here
    - Effect: Allow
      Action:
        - cloudwatch:DescribeAlarms
        - cloudwatch:GetMetricStatistics
        - ec2:DescribeAccountAttributes
        - ec2:DescribeAvailabilityZones
        - ec2:DescribeSecurityGroups
        - ec2:DescribeSubnets
        - ec2:DescribeVpcs
        - ec2:CreateNetworkInterface
        - ec2:DescribeNetworkInterfaces
        - ec2:DetachNetworkInterface
        - ec2:DeleteNetworkInterface
        - sns:ListSubscriptions
        - sns:ListTopics
        - sns:Publish
        - logs:DescribeLogStreams
        - logs:GetLogEvents
      Resource: "*"

functions:
  paypalBraintreeFinalize:
     handler: handler.paypalExpressFinalize
     events:
      - http:
          path: paypal/finalize
          method: post
          cors: true

  paypalBraintreeToken:
     handler: handler.paypalExpressToken
     events:
      - http:
          path: paypal/token
          method: get
          cors: true

  stripeCheckout:
    handler: handler.stripeCharge
    events:
     - http:
         path: charge/stripe
         method: post
         cors: true

All in all, not that bad. You can read through this and probably figure out that I have functions to handle a Stripe charge and some PayPal stuff. They’re exposed via the API Gateway (which the framework does for you) and I’m able to wire up CORS easily.

Unfortunately I also want to talk to a database that’s not DynamoDB. I like using PostgreSQL with Compose.com which means that if I want my Lambda functions to talk to the outside world I need to set up a gateway, a VPC and all the lovely machinery that goes along with it.

The super silly thing is that I need to do this even if I use Amazon’s RDS stuff – you have to have a VPC setup for security reasons. That aint cheap.

VPCs and gateways are not cheap but, in the grand scheme of things, $50/month isn’t so bad either. But if I’m going to pay that… why don’t I just use Heroku?

Back Where I Started

It took me about 10 days to get things running properly. 2 of those days were spent being very, very frustrated trying to figure out all of the moving pieces and dealing with errors like this:

This was the final capper for me:

servless --help

WARNING: You are running v1.7.0. v1.8.0 will include the following breaking changes:
  ...

This doesn’t make me feel excited about the team behind this thing. Breaking things on a point release goes against the whole idea of semantic versioning. It wouldn’t matter but the framework uses Node/NPM which by general agreement follows semver, so it seems a bit silly to ignore it.

A bug report was filed and completely ignored, aside from this email response:

Breaking Changes – We’re no longer following strict semver. You can always find the most recent list of breaking changes in the upcoming milestone or in the Serverless CLI.

So there’s that then. Would you base a business on this framework? I decided it wasn’t for me.

Rolling My Own

Once you understand the machinery that goes into AWS Lambda, rolling your own isn’t too difficult. I took a weekend to dive in and figure out the bits that confused me prior, and eventually I had a few shell scripts rolled together that orchestrated most of what I needed.

If you’re comfortable with scripting, know AWS and can tolerate some head-pounding… AWS Lambda isn’t all that bad. A bit more expensive then I’d like but… not that bad.

Verdict: Every single time I’ve used AWS I’ve had to ditch a weekend or 2 remembering how things worked. It’s a powerful system, but in many ways is overkill when compared to something like Heroku (or the services like Heroku). The problem is that Heroku can get expensive, fast.

Then there’s what I’ve been doing for the last 5 years or so that’s working great: DigitalOcean. It has been my go-to for so long; it takes a lot to justify moving away from them. I have my build scripts down cold and I can whip up a server during lunch, complete with SSL and multiple, managed Node processes. I have the same for Elixir too.

Honestly: serverless with AWS isn’t buying me anything. I don’t want to use DynamoDB (though I’m sure it’s wonderful), I can use a message queue if I want to do things in a “small function” kind of way, and honestly it just takes too much space in my brain.

All of that said, yes I know that new frameworks are popping up daily, so maybe things will change a bit. For now, no AWS Lambda for me.

And Then: Firebase

I remember reading this post with a groan, thinking “not again”…

Today we are excited to announce the beta launch of Cloud Functions for Firebase. It lets you write small pieces of JavaScript, deploy them to Google’s Cloud infrastructure, and execute them in response to events from throughout the Firebase ecosystem.

I’ll admit to a heavy amount of Magpie-ishness. I can’t help it… it’s just me and I’ve learned to let me be me… as opposed to you or anyone else.

The first impression I had when looking over the “new” Firebase site was one of immense relief. The page is elegant, focused, easy on the eyes and easy to understand. The console is wonderfully simple, too:

Clicking through each of the services made sense to me. But what about the functions? How simple would it be to get code up here? What about hosting? Can I SSL this stuff? Outbound networking? Can I use PostgreSQL instead of Firebase or, maybe, together with it?

I found out the answers to all of these questions was, in short: simple, simple, yes, yes and yes. After working with AWS, Firebase was a very welcome change.

I’ll get to all of that in the next post.


See this series as a video

Watch how I built a serverless ecommerce site using Firebase. Over 3 hours of tightly-edited video, getting into the weeds with Firebase. We’ll use the realtime database, storage, auth, and yes, functions. I’ll also integrate Drip for user management. I detest foo/bar/hello-world demos; I want to see what’s really possible. That’s what this video is.