Building a Twitter Reader with a Serverless API

Using Netlify and Up to abstract the pain away from deploying software

Context

Scott Riley and myself wanted to build a cut-down Twitter reader, to serve as a pseudo-RSS feed for your timeline. We called it Readme App.

We used Netlify to deploy, build and host the client-side Vue.js app, and Up to deploy (and build) the Node.js API to AWS.

If you want to poke around the source code, check out the Vue.js app and the Node.js api on GitHub.

Serverless APIs with Up

What in the heck is a serverless API? Well, nicking this straight from Wikipedia:

Serverless computing is a cloud computing execution model in which the cloud provider dynamically manages the allocation of machine resources. Pricing is based on the actual amount of resources consumed by an application
Serverless computing still requires servers. The name “serverless computing” is used because the server management and capacity planning decisions are completely hidden from the developer or operator.

In tangible terms:

  1. You write code, you run up
  2. ….nope, that’s it

You, the happy developer, doesn’t have to worry about servers or infrastructure, you just use a tool (like Up) to manage the deployment and “serverless” setup to put your code on platforms like AWS, which will run the code when invoked.

In Readme App, to push the latest API code, I cd into the project and run up deploy production , watch a little spinner animation for a few seconds, and BAM, the latest API code is up, ready to be called by client-side code (or, other APIs?). When the API is hit with a HTTP request, AWS will spin up magic computers to run the API, my code will do what it needs to, return some JSON to the client, and AWS will shut everything down. I get billed for that computation/execution time.

Keeping this Up-specific, the only time you really need to interact with AWS is to grab some initial keys and make them available for Up, so it can manage the deployment and infrastructure to/on AWS on your behalf.

If you’re a fan of Heroku, and the hassle-free experience of a PaaS, well, are you in for a treat with FaaS. Functions as a service. I like to think of it more of functionality as a service, as opposed to straight up functions, ie. login() . In Up, the entire communication with the Twitter API is housed in one single AWS Lambda function. I just sent it different GET params to make it do different thing, ie. fetch tweets, fetch the user profile, log a user in, etc…

So what does the code look like that we deploy? Well, Up allows you to deploy vanilla Node.js HTTP servers, without having to worry about AWS Lambda-specific syntax (which is what’s normally required when working with Lambda). Cut to it’s bare essentials, this is the Readme App API:

const http = require('http')
const {
PORT = 3000
} = process.env
http.createServer((req, res) => res.end(someData)).listen(PORT)

Of course, the above won’t do much, and won’t work at all, but you get the idea. We get access to req and res that we’re familiar with, and everything in-between is up for grabs.

For Twitter access, I confined everything to a custom Twitter class, which is highly generic and re-usable. It does, however, do some formatting and data transforming, to make things a little more suited for our Readme App client.

This class is instantiated, and various methods invoked, by the router, based on different GET params.

I decided it would be a good idea to write a tiny little router, whereas in hindsight, I would actually use Micro Router. But you can see below that our API receives POST data from the Vue.js client, and passes them onto the Twitter class to interact with the Twitter API:

const handler = type => {
return {
oauth_request: async () => {
const twitter = new Twitter()
return await twitter.getOAuthRequestToken()
},
auth_token: async ({ token, tokenSecret, verifier }) => {
const twitter = new Twitter()
return await twitter.getOAuthAccessToken(token, tokenSecret, verifier)
},
tweets: async ({ accessToken, accessTokenSecret, userId, latestId }) => {
const twitter = new Twitter()
return await twitter.fetchTweets(accessToken, accessTokenSecret, userId, latestId)
}
}[type]
}

As far as the API goes, that’s about it. When you deploy with Up, it gives you a URL from AWS, which you can use to ping or call your API. I then use this URL in Netlify’s dashboard as an environment variable, which is illustrated below…

Netlify and Vue.js

Netlify really is a phenomenal service, for which I’m not going to copy/paste the full set of features, but implore you to go check it out for yourself.

When I want to deploy the latest Vue.js client changes for Readme App, I just push to the master branch on GitHub. That’s it. I’ve set it up in Netlify so it tracks the master branch of readmeapp-site , and will run the build command, that I set up, on the latest codebase. Then it will take that output code and push it to their CDN. Maaaaaaagic.

The Readme App could function just as well with any JS framework…or no JS framework. However, I’ve been using Vue.js on a client contract for the past 7 months, so it was my flavour of choice for building a client-side app quickly. I feel like the best thing about Readme App is that it keeps everything about your session stored in LocalStorage. This means that once we fetch your latest tweets, we sync all the data with LocalStorage. THIS MEANS, you can theoretically use Readme App offline (you just won’t see any new tweets, just your existing timeline)

Also, your Twitter oAuth keys are save in LocalStorage. Essentially, the API is stateless. I like this a lot. It just receives a request for say, tweets, and the relevant oAuth keys, and then calls the Twitter API. Then it shuts down, and its up to the client app to store/manage the data it received from the API.

Offline (LocalStorage syncing) is made easier with vuex-persistedstate, and used in Readme App like so:

export default new Vuex.Store({
actions,
getters,
state: initialState,
mutations,
plugins: [createPersistedState({
paths: [
'user',
'oAuthToken',
'oAuthTokenSecret',
'oAuthAccessToken',
'oAuthAccessTokenSecret',
'tweets',
'lastFetched',
'theme'
]
})]
})

Netlify lets you use environment variables, which is great, because I can develop Readme App locally against a locally running API, and then push to GitHub and have Netlify run the app and pass it the relevant values, like so:

This is the API URL that Up gives us from AWS

Which I have set up in the client-side app Webpack config, like so:

plugins: [
new webpack.DefinePlugin({
'process.env.API_URL': JSON.stringify(process.env.API_URL || 'http://localhost:3000/'),
})
]

The Future…is here

I see these singular stateless APIs as incredibly powerful. With a little tidying, I could have a re-usable, isolated API online which talks to Twitter. This could be plugged into any project I develop. For example, imagine writing a Facebook API class once, and only once, deploying it with Up as an API, and then any project that needs to log a user in with Facebook could use this. Obviously, you’d need to think about App IDs etc…but there’s definitely mileage in building these Lego-esque pluggable APIs.

I don’t want to manage infrastructure, I don’t enjoy it. I do enjoy writing code. Up takes over at that point and does the rest for me. Bitchin’.

Like what you read? Give Ben Howdle a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.