Webtask — An Alternative For A Back-end
If you follow web-development trends, you’ve probably stepped across the words: FaaS (Function-as-a-Service), serverless, AaaS (Authentication-as-a-Service).
There are two main protagonists in computing: (i) The code and (ii) Everything else that would run this code; it can be hardware resources, run-time, OS etc. Then server-less computing would mean only “The code” for the developer, everything else would depend/managed by the cloud provider. It is easier to understand it as Resource-less computing.
And here’s what the guys from Auth0 (parent of Webtask) say about Webtask:
All you need is code
Project
In this tutorial we’ll build one of the projects from Free Code Camp curriculum — Nightlife Coordination App. If you take a look at user stories you’ll see that requirements for this project are:
- an unauthenticated user, can view all bars in his/her area
- an authenticated user, can add himself to a bar to indicate he is going there tonight
- an authenticated user, can remove himself from a bar if he no longer wants to go there
- an unauthenticated user, when he login he should not have to search again
A Few Words About Front-end
For the front-end I’ve built SPA with VueJS, that will make HTTP calls to the back-end and display results.
The authentication is handled by Auth0 Lock. Auth0 provides AaaS (Authentication-as-a-Service). As a developer you register with the service, create your client, get your keys, include them in your code and hand over the authentication process to Auth0 service.
For the purpose of this project our client will make three different POST requests to a back-end API:
- POST request to send a query for the location to search for a bars
- POST request to modify bar data that user wants to visit, by adding
user.name
to the array of visitors - POST request to modify bar data of the bar that user doesn’t want to visit any more by removing
user.name
from the array of visitors
The last two request can be made only from an authenticated user and Authorization Headers have to be sent. Calls are made with axios
module.
To make things simple, no vue-router or vuex is used. Everything is at the same route and user info is stored into the localStorage
as well as the last search results, so that the user doesn’t need to search again after he/she logs in (fulfillment of the user story Number 4).
You can see the code for the front-end here.
Express Back-end API
On the back-end we will build Express API that does a search for the bars at the particular location when the POST request carrying a query for the location is received from the client, using Yelp API interface. When the search is finished and the results are stored in an array of bars, an async querying of the Database is taking over. The Database is searched for each bar in bars array, based on its yelpId
. If bar is found in Database visitors
property of the bar in the bars array is replaced by the value of visitors
property of bar in Database, otherwise everything stays the same. Only when the previous process is finished for each single entry in the bars array is the bars array returned as res.json
. This request is made without requiring authentication.
The second route in our API is for the authenticated user to visit bar. When the request is received to this route our application does a search for the bar by yelpId
. If it is found in the Database it gets updated with data from req.body
and barUpdated
is returned as res.json
to the client. If there’s no such a bar in Database, our application creates one with req.body
data and returns that bar to the client.
The third route receives POST request from the client to cancel an authenticated user visit to the bar, so bar is found in the Database, visitors
property is updated with the data from the req.body
and updatedBar
is finally returned to the client.
Complete back-end folder structure is as follows:
/middleware
index.js
/models
Bar.js
/routes
bar.js
index.js
package.json
Let’s Enter The Fun Part — Webtask
So far our code doesn’t differ much from Express back-end API except for this weird req.webtaskContext.secrets
part which you’ve probably noticed in the routes/bar.js
file above and which we are going to explain later on.
So, ladies and gentlemen, let’s introduce Webtask!
Webtask provides Node.js CLI tool to make deploying your FaaS and interacting with the webtask API easy peasy. So type in your console:
npm install wt-cli -g
Go to Webtask and login. You’ll be asked for an e-mail or phone number to send you an activation code.
wt init <YOUR-EMAIL>
When prompted type your activation code and you are ready to create your first webtask.
Create project folder and place inside index.js with a following content:
module.exports = function (cb) { cb(null, “Hello world”);}
cd
into your folder and type in your console:
wt create index
Hit the URL logged in the console and admire your deployed app.
Webtask Programming Models
We’ve just experienced the basic webtask programming model — exporting function with a single argument: a callback. On completion function calls the callback with two arguments: error and result, and an error or result is returned to a caller as application/json
content type.
Another programming model allows us to bind an Express app to a webtask context and export that app. For that to work Webtask provides us with utility tool called webtask-tools
. Go ahead and meet a webtask-tools, npm install it and require it in your index.js
If you look at the code above you can see that main difference with classic Express app is that instead of listening we now export the function created from Express app.
Database
The next cool thing about Webtask is that we can use an actual Database like with any project hosted on Heroku or elsewhere. For this project we will use MongoLab to host our Database. So go to MongoLab, sign up, create Database for our project and Add database user.
Mongoose Schema
Mongoose makes it easier to create, connect, query and update Database, so we’ll npm install it and make our Schema for Bars collection.
Connection
We need our app to connect to a Database at the beginning of each request and disconnect at the end. We will create middleware folder with an index.js
file which will be required and used in apps entry file (index.js
) before routes:
req.webtaskContext And secrets
is the way to access context object of your webtask when you use webtask-tools. The context object has as its own property secrets object where you can store and retrieve sensitive information like tokens, URLs, Access keys. The secret information can be passed via terminal when creating webtask
wt create index — secret MONGO_URL=<MONGOLAB-CONNECTION-URL> — bundle
Another way to pass the secrets (especially if you have many of them) is by creating a secrets file in the root of your project
and then running
wt create index — secrets-file secrets — bundle
After running the command above you’ll get URL similar to below
https://{your-subdomain}.run.webtask.io/{your-webtask-name}
that you can access from the client. You can publish your front-end on Github Pages or Surge. I published mine on Surge.
Bonus: Authentication
We’ve already done authentication with Auth0 service on the front-end, but we can also protect access to our webtask with Auth0. If you change
module.exports = wt.fromExpress(app);
to
module.exports = wt.fromExpress(app).auth0();
end add your credentials to the secrets file (AUTH0_CLIENT_SECRET, AUTH0_CLIENT_ID, AUTH0_DOMAIN), your webtask will be associated with Auth0 application and only authenticated callers will be able to invoke your Express route handlers.
The important thing that took me a lot of digging is: as soon as you associate your webtask with Auth0 using those two steps above the API endpoint you address your calls to, changes from:
https://{your-subdomain}.run.webtask.io/{your-webtask-name}
to
https://webtask.it.auth0.com/api/run/{your-subdomain}/{your-webtask-name}
This is something that I didn’t find clearly stated in the official documentation, but discovered after tying some ends and searching through their Slack channel. So it might be a good idea to include this important guidance in the official documentation to avoid further confusion.
Furthermore, we want to allow an unauthenticated user to search for the bars so we can exclude that route from authentication. We’ll also provide loggingError
function, so we know what’s going on when an unauthenticated caller is trying to reach our protected routes
One thing that helped me find what’s wrong was running wt logs
in the terminal. If that wasn’t enough there was the Slack channel - Quick response and right to the point.
The Github repository for this project is here.