Build an automatically updating Twitter Header with NodeJS and Twitter API
Tags: twitter, node, api, tutorial
Published: 2022-03-09 09:38:57

---
title: Build an automatically updating Twitter Header with NodeJS and Twitter API
published: true
description: Step-by-step guide how to build an automatically updating twitter header which shows the latest followers
tags: twitter, node, api, tutorial
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7722ivq545gynpylc94j.jpg
---

This tutorial shows you how to build an application which automatically updates your Twitter Header with your latest
followers.

GitHub Files: https://github.com/dom-the-dev/twitter-banner

> [Subscribe to my YouTube Channel](https://www.youtube.com/domthedeveloper?sub_confirmation=1)

### I also made Step-by-Step Video <a name="video"></a>

{% youtube jl9OKxoqVcA %}

#### List of contents

- [Twitter Elevated Access](#twitter-elevated)
- [Setup application](#setup)
- [Twitter client](#client)
- [Get Followers](#followers)
- [Save Follower Images](#images)
- [Create Banner](#create-banner)
- [Update Banner](#update-banner)
- [Cron Job](#cron)

### Twitter Elevated Access <a name="twitter-elevated"></a>

Before we can start, we have to create an application on
the [Twitter Developer's Platform](https://developer.twitter.com/en/portal/dashboard) (twitter account required). On
your Dashboard you should now see a small teaser asking you "Want more API access?". Click on "View products".
Alternatively, in the left panel click on "Products" and from the dropdown select Twitter API v2. You will then
automatically get to the Tab where you can apply for the elevated access. If not, in the top center of the page, choose
the elevated tab to see the button which says "Apply for Elevated".

Then you have to go through some steps where you need to fill out some information about you and your application. After
confirming the terms on the last page, you either get elevated access granted or not. So make sure to fill out the
information wisely.


### Setup application <a name="setup"></a>

Now, we are ready to set up our node application. Make sure that node is installed.

Run `node --version` in your terminal. If you get a node version number printed, node is installed. If not, you need to
go to the [node homepage](https://nodejs.org/) and download the installer.

Once node is installed, we can create a new node application. In your terminal, create a new directory and switch into
it.

```CLI 
mkdir twitter-banner && cd twitter-banner
```

Then run the following command to initialize a new npm project. This will create a package.json inside the project
directory.

```CLI 
npm init -y 
```

Now you can open the project in your text-editor/IDEA.

In the root of the directory create a new file called `index.js` and add a simple `console.log("hello world")` in order
to test if node is running correctly.

Then in your terminal run the following command

```CLI
node index.js
```

Your terminal should now prompt "Hello world".


### Twitter Client <a name="client"></a>

Let's now create a Twitter Client, so we can simply communicate with the Twitter API. For that install a npm module
called [twitter-api-v2](https://www.npmjs.com/package/twitter-api-v2).

```CLI
npm i twitter-api-v2
```

After the installation we can create our client. Create a file called `twitterClient.js`
and open it. Now we need to require the module and instantiate a new object with our twitter keys, which we get in a
second.

The code should look like this, where the current keys will be replaced by the actual keys.

```javascript
const {TwitterApi} = require("twitter-api-v2")

module.exports = new TwitterApi({
    appKey: "<your-api-key>",
    appSecret: "<your-api-secret>",
    accessToken: "<your-access-token>",
    accessSecret: "<your-access-secret>"
})

```

To get the keys we need to switch back to the developers platform of twitter. On the Dashboard you can click on "+
Create Project" and walk through the steps. On the last step you get the first keys displayed.

Copy and replace them with the keys in your client.

API Key = appKey

API Key Secret = appSecret.

In order to get the access token and the access secret we have to adjust some settings. On the overview page where your
project are listed click on the gear icon to get to the settings page of your app and click on Edit in the "User
authentication settings"-Section.

To be on the safe side, activate OAuth 2.0 and OAuth 1.0 and fill out the rest of the form. App permissions need to be
set at least to "Read AND Write permissions".

Important: You can't set the Website URL to localhost, instead you need to set your local ip address. You can find it
with your terminal with the following command:

Windows

```bash
ipconfig
```

Linux/Mac

```bash
ifconfig
```

At the end of the page click on save. You won't need the client ID and Secret which will be shown now. Instead go back
to the settings page of your app and choose the "Keys and Tokens" tab. In the section "Autentication Tokens" you now can
generate the Access Token and Secret with Read/Write persmissions. Click on generate and copy/paste the keys to your
client.

To test if the twitter client is working, let's create a test-tweet. Go to `index.js` and require the twitter client at
the top of the file.

(Remove the console.log() if you want)

```javascript
const client = require("./twitterClient");
```

And create a asynchronous function which calls the tweet method of our client where we paste the message to tweet. This
could look like this

```javascript
async function testTweet() {
    await client.v2.tweet("Hi, I am building a twitter bot!")
}

testTweet()
```

Now run `node index.js` again in your terminal. If everything works fine you can check your twitter account for this
tweet. You can now delete or comment this function out.


### Get Followers <a name="followers"></a>

Now, let's create a new file `twitterController.js` where we gonna collect the methods, talking with the Twitter API. In
this file again require our twitter client:

```javascript
const client = require("./twitterClient");
```

So, the first step of creating our banner will be to get our latest followers. Let's create an async function called
`getFollowers()` where we call the followers method of our client and return the latest 5 followers:

```javascript
async function getFollowers() {
    const followers = await client.v2.followers("<YOU-TWITTER-ID>")
    return followers.data.slice(0, 5)
}
```

As you see here we need to paste the twitter-ID from our account. To find this, we can go
to [tweeterid.com](https://tweeterid.com/). Paste your twitter @ in the input field and click on convert. Then
copy/paste the ID to the followers function.

At the end of the file need to export this method:

```javascript
module.exports = {getFollowers}
```

Switch to `index.js` and require this method:

```javascript
const {getFollowers} = require("./twitterController")
```

Let's already create the wrapper function for the banner-process where we are going to save the followers in a variable:

```javascript
async function generateBanner() {
    const followers = await getFollowers()
}
```


### Save Follower Images <a name="images"></a>

The next step is to get the avatars of your followers, and save them to the filesystem. Let's do so by create a new
async function so called `getProfileImageUrl` in the `twitterController.js`

This function accepts one parameter, the user ID of a follower, and returns the profile image URL.

```javascript
async function getProfileImageUrl(user_id) {
    const {profile_image_url} = await client.v1.user({user_id})
    return profile_image_url
}
```

Add this function to the exports:

```javascript
module.exports = {getFollowers, getProfileImageUrl}
```

Now create a new file called `imageController.js`, here we are going to collect all methods which manipulate images.

For this steü, we need to install two more packages, [axios](https://www.npmjs.com/package/axios)
and [sharp](https://www.npmjs.com/package/sharp)

```CLI
npm i axios sharp
```

Require them at the top of `imageController.js`

```javascript
const axios = require("axios")
const sharp = require("sharp")
```

Now the next function is going to fetch the images with axios as an arrayBuffer and paste this to the sharp
method, which helps us to the images to our  file system with the resolution 100x100.
The function gets two params, a url and a name. The url will be the profile-image-url we get from the previous function. The name is used to save the image.
Don't forget to export this function.

```javascript
async function saveImage(name, url) {
    const {data} = await axios.get(url, {
        responseType: "arraybuffer"
    })

    await sharp(data).resize(100, 100).toFile(`./images/${name}.png`)
}

module.exports = {saveImage}
```

Let's combine the two methods in `index.js` to finally save the follower avatars.

In the `generateBanner()` method, we are going to iterate over the followers array which we already have with a for/of loop.
We use the for/of since we can you async inside of it. For each follower we gonna get the profile-image-url and paste it to the `saveImage()` method in order to save the images.

```javascript
async function generateBanner() {
    const followers = await getFollowers()

    for(const follower of followers) {
        const url = await getProfileImageUrl(follower.id)
        await saveImage(follower.id, url)
    }
}
```

Before you run this, you need to create an `images` directory. Otherwise sharp doesn't know where to save the images.
If you run generateBanner() now you should see the images saved to your file system.


### Create Banner <a name="create-banner"></a>
For this step, you are going to need a twitter template. It needs to have the resolutions 1500x500.
You can find mine example [here](https://domiderdude.de/banner.jpg). Save it to the root of your directy.
In my example i am going to name it `banner.jpg`.

On this template we are now going to place the follower images at. To do so we need to install another npm package 
so called [Jimp](https://www.npmjs.com/package/jimp).

```javascript
npm i jimp
```
Besides this module we need to add promise based version of `fs` from node in order to perform async operations on. 
Add them at the top of `imageController.js` like this:

```javascript

const axios = require("axios")
const sharp = require("sharp")
const Jimp = require("jimp")
const fs = require("fs")
const fsPromises = fs.promises

```

The `createBanner()` function which we now create, uses Jimp to create a new image from our template`banner.jpg`.
Then it iterates over all saved avatars and places them on the new created banner-image.
An index variable will be used to move each picture a bit so they won't be places on top of each other. At the end the new file will be saved.

This function should look like this:

```javascript
async function createBanner() {
    const banner = await Jimp.read("./banner.jpg")
    const files = await fsPromises.readdir("./images")

    let index = 0;
    for (const avatar of files) {
        const imgPath = `./images/${avatar}`
        const image = await Jimp.read(imgPath)

        const position = 475 + index * (100 + 10);
        banner.composite(image, position, 380);

        index++
    }

    await banner.writeAsync("./final.png");

}

module.exports = {saveImage, createBanner}
```

Add this it to `index.js` which now should look like this: 

```javascript

const {getFollowers, getProfileImageUrl} = require("./twitterController")
const {saveImage, createBanner} = require("./imageController");

async function generateBanner() {
    const followers = await getFollowers()

    for(const follower of followers) {
        const url = await getProfileImageUrl(follower.id)
        await saveImage(follower.id, url)
    }

    await createBanner()
} 

generateBanner()

```

You can again test-run this method to check if the new banner will be created correctly. Look for `final.png`

### Update Banner <a name="update-banner"></a>
Let's upload this cool header to our twitter profile now!

In `twitterController.js` create the `updateBanner()` method. Here we are calling the update profile banner method, paste the path to our image and the resolutions:

```javascript
async function updateBanner() {
    await client.v1.updateAccountProfileBanner("./final.png", {
        width: 1500,
        height: 500
    })
}

```

```javascript
module.exports = {getFollowers, getProfileImageUrl, updateBanner}
```

Require this method and call it in `generateBanner()` in `index.js`

```javascript
const {getFollowers, getProfileImageUrl, updateBanner} = require("./twitterController")
const {saveImage, createBanner} = require("./imageController");

async function generateBanner() {
    const followers = await getFollowers()

    for(const follower of followers) {
        const url = await getProfileImageUrl(follower.id)
        await saveImage(follower.id, url)
    }

    await createBanner()
    await updateBanner()
}

generateBanner()
```

Again, you can test run this with `node index.js` in your terminal. When you now switch to your browser and reload your twitter profile,
you should be able to see the new banner.


### Cron Job <a name="cron"></a>

The last step will be, to create a so called CronJob which performs given actions at a given time. In our example we are going
to check for the latest followers every minute.

Install the npm package [cron](https://www.npmjs.com/package/cron):

```bash
npm i cron
```

And require it at the top of `index.js` like that:

```javascript
const CronJob = require("cron").CronJob
```

Now we create a new object from the CronJob class, where we pass two params. 
The first one is a string and declares, when the job has to run.
A great tool to set up the time is [CronTab Guru](https://crontab.guru/#*_*_*_*_*).
The second parameter is the callback function which we want to be called.
In our case the `generateBanner()` method. Start the job by calling the `start()` method of our job object

```javascript
console.log('starting node app')
const job = new CronJob('* * * * *', async function() {
console.log('start generating banner')
generateBanner()
})

job.start()
```

And that's it! If you are facing any issues, leave some feedback in the comments or hit me up on twitter @Dom_TheDEv