How I Built a Twitter App for the APEX & ORDS Community

Search for a command to run...

Hi Jon Dixon,
I am trying to fetch my personal tweets using api.twitter.com/2/tweets/search/all?query=KinjanBhavsar but I keep on getting Unauthorized even though I pass the bearer token in Authorization.
I have a requirement to fetch the tweet for my account and look for a keyword and create a simple ticket in the system.
Could you please suggest what I could be doing wrong?
Nice project! Thanks for letting me know about oracle free cloud. Seems useful for prototypes
It seems like a great project!
On congrats on making it to week3, looking forward to the next article :)
Thank you so much for your work. You bring the APEX world forward with so many nice and good articles. :-) Thank you so much Jon.
Introduction One of the marquee features of APEX 26.1 was AI Interactive Reports. When I started testing this new feature, I was interested to see what was going on behind the scenes. In this post, I

Introduction One of the marquee features of APEX 26.1 is APEX AI Agents. APEX AI Agents allow you to use an LLM and your own PL/SQL and JS tools to perform actions on your data instead of just chattin

Introduction In my previous post, I looked at logging APEX AI Agent requests and responses with Request and Response handlers. Logging is the first step because it shows you what is actually moving th

Introduction Logging is one of the first things you need when building serious AI Agents. Without it, debugging quickly becomes guesswork. You can see the final answer, but not always why the model ch

Context management patterns from building agents in Oracle APEX

Note (18-MAR-2023): When Twitter announced they were going to charge for using their APIs, I closed down the Twitter App that I mention in this post. Aside from this, the content remains relevant.
This post will describe the Web App I built to track Twitter activity for Oracle Application Express (APEX) and Oracle REST Data Services (ORDS). The goal of the App is to feature developers that contribute to these communities via Twitter and also to understand better how the community uses and responds to Twitter content.
π The bonus for me is that this project allowed me to use the development tools I love the most to help the developer community.
π You will learn how to set up a Twitter Developer account, call several Twitter APIs via Postman, and see the inner workings of the App itself. I have also made the code available from this GitHub Repositiory.
Let's start with a brief screen recording of the App.

This section describes the software the App was developed with and the hardware on which it is running.
The App was built using Oracle Application Express (APEX) version 22.1 running on Oracle Autonomous Database and Oracle REST Data Services Version 22.2.
Oracle APEX is a low-code development platform that enables you to build scalable, secure enterprise apps with world-class features that can be deployed anywhere. Using APEX, developers can quickly develop and deploy compelling apps that solve real problems and provide immediate value.
The App is running on the Oracle Cloud Infrastructure (OCI) Free Tier Autonomous Database Service at the cost of π²0 per month. Read my post for more details on this architecture.
In this section, I will focus on some of the critical design elements for the App.
From the beginning, I wanted the App to run just as well on mobile as on desktop. APEX's out-of-the-box Progressive Web App (PWA) and Responsive UI capabilities made this possible without any additional coding on my part.
Even though the Tweet and Tweeter data is being fetched from Twitter over REST, I wanted to ensure the App was fast. I did this by caching much of the Tweet-related data (Tweets, Re-Tweets, Quoted Tweets) in tables on the Oracle database in which APEX runs.
Local table caching was made possible using APEX Automations, which allowed me to quickly create an hourly schedule to capture the latest Tweet and Tweeter information.
Caching data locally makes searches and charting super fast. Detailed information about a user's profile is fetched over REST in real-time if the Tweeter profile has not already been captured.
The data model is simple and consists of four tables
CNDEMO_TWT_CAPTURE
CNDEMO_TWT_TWEETS
CNDEMO_TWT_RELS
CNDEMO_TWT_USERS
The table CNDEMO_TWT_CAPTURE stores the hashtags and @ mentions, I want to capture. The hourly capture process uses this table to determine which Tweets to search. Having this flexibility allows me to easily add additional hashtags in the future.
This section focuses on the Twitter APIs that I used to capture the data used by the App.
Signing up for a developer account was easy. You can get started by following this link.
Optionally signup for 'Elevated' access. Amongst other things, 'Elevated' access allows you to query 2 million Tweets/month instead of the Standard 500k. Use this link to compare the different plan levels. Aside from having to complete a brief questionnaire, upgrading to 'Elevated' access was straightforward. 'Elevated' access was granted almost immediately after submitting the questionnaire. I am assuming this was an automated approval.
You will also find these links useful when getting started with the Twitter APIs:
After signing up for a developer account, you must create an App within a Project. In the below screenshot, you can see I have one App called 'Cloud Nueva APEX Demo' in the project 'Cloud Nueva'.

Once you create an App, Twitter will present you with 'Keys & Tokens'; keep these in a safe place. We will use the 'API Key' and 'API Secret Key' shortly. The screenshot below shows the end of the Wizard process to create a new App (the process is straightforward).

Don't worry; I have already deleted all of the API Keys and Bearer Tokens you may see in this post. π
We can then use these API Key and the API Secret Key keys as part of the 'OAuth2 Client Credentials' flow in Postman to get an access_token as follows:


Note: Unlike many OAuth services that expire the access_token (also known as the Bearer Token) after a period of time, Twitter does not expire this access_token. If you call https://api.twitter.com/oauth2/token multiple times, you will get back the same access_token. This is in fact the same 'Bearer Token' generated when we created the 'App' in the Twitter Developer Account in the previous step.
To revoke the Bearer Token / access_token, you can click the 'Revoke' button from the Twitter Developer Portal as seen in the screenshot below.

π You can read more about using the 'App only authentication and OAuth 2.0 Bearer Token' authentication method here
The main API is the Tweet Search Recent API. This API allows you to search Tweets created in the past seven days.
The seven-day search limit is one notable limitation of even the 'Elevated' level Twitter Developer Account. As you will see later, I have worked around this by implementing a nightly process to loop through all tweets captured between seven and twenty-one days ago and call the Tweets Lookup API to update them. Looking back at older tweets was another reason I chose to store Tweets in the Oracle database.
The following screenshot shows how to fetch Tweets with a mention of @oracleAPEX or the hashtag #orclAPEX.


access_token obtained in the authentication step.Pagination
Pagination is handled via the max_results and the next_token query string parameters.
Passing max_results of 100 (the maximum value) will fetch up to 100 Tweets at a time (assuming your search returns 100 or more Tweets.
The meta section at the end of the API response includes the number of Tweets returned by the search. If there are more than 100, then the API will also include a field called next_token.
"meta": {
"newest_id": "1562224116723490817",
"oldest_id": "1561742996932108290",
"result_count": 100,
"next_token": "b26v89c19zqg8o3fpz8kbizzndmockeoekrzulr87t3zx"
}
If you call the API again and pass in a parameter called next_token with the value you just received, the API will fetch the next 100 Tweets. This way, you can fetch as many search results as you like (within your account limits).

The other API, the APEX App, uses extensively is the Users Lookup API. The App uses this API to fetch details for users that have Tweeted about APEX or ORDS. It allows you to pass a list of up to 100 usernames and returns details for these usernames.
The screenshots below show the Postman setup to fetch details for these two users jondixonus,cloud_nueva.


Now that we understand the Twitter APIs, we can look at some of the more significant code behind the App. I don't plan to explain every line of code, so I encourage you to download the code from my Github Repository now.
We first need to create some APEX Web Credentials to store the Twitter OAuth2 Client ID and Client Secret. APEX Web Credentials make securely storing credentials like these super easy.

π See my post Secure your Secrets with APEX Web Credentials for more details on APEX Web Credentials.
APEX Automations allow you to schedule code to run (locally or on a remote database) based on an event or schedule.
π See my post Scheduling Made Easy with APEX Automations for more details on APEX Automations.
The Twitter App uses two Automations:
Capture Tweets - Hourly
Refresh Tweets and Users - Nightly
Capture Tweets - Hourly
This Automation calls the procedure cndemo_twtr_utl_pk.capture_tweets every hour. The procedure uses the APEX PL/SQL API apex_web_service.make_rest_request to call the Twitter Recent Search API, fetching all APEX-related Tweets for the past seven days in batches of 100.
Any Tweets not already in the database are added, and any existing Tweets are updated.
The reason for the update is to update Tweets that have received additional likes etc., since their original capture.
Refresh Tweets and Users - Nightly
This Automation runs nightly and calls two procedures, one after the other.
refresh_tweets
This procedure calls the Tweets lookup endpoint to get updated data for Tweets in my database between 7 and 21 days old.
The 'Tweets lookup' API allows you to submit up to 100 Tweet ids to fetch details for. It will fetch details for these Tweets no matter how old they are.
Fetching details of specific older tweets helps me get around the problem that the Search Recent API only beings back Tweets for the past seven days.
I assume that any Tweets older than 21 days are not worth updating.
refresh_users
This procedure uses the Users lookup endpoint to get updated follower counts etc., for all users that have Tweeted about APEX.
The 'Users lookup' API allows you to submit up to 100 User ids to fetch details for. The procedure loops through all users in the Oracle database, calling the 'Users lookup' API in batches of 100.
Batching Users and Tweets into fewer REST API calls reduce network round trips and significantly improves performance.
The bulk of the code in the package cndemo_twtr_utl_pk is used in the Tweet and Tweeter capture Automations. I have added many comments to the code, so hopefully, it is self-explanatory.
This page uses the APEX Cards Region to summarize information for each configured search. I am utilizing APEX Template Directives to present a summarized information table in the center of each card.

π See my post Client-Side Formatting with APEX Template Directives for more on Template Directives.
This page highlights the most active Tweets and Tweeters. I implemented a scoring system based on Original Tweets Vs. ReTweets and Likes to illustrate the most effective Tweets and Tweeters. The page allows you to easily navigate to specific Tweets so that you can like or re-tweet them. I used APEX Classic Reports to build the four regions on this page.

You can click on a username to view their Twitter profile information.

This page shows three charts illustrating week-by-week Twitter activity and the most popular days of the week to tweet. When you visit this page, it calls the procedure page30_load, which summarizes Tweet data into an APEX Collection.
APEX Collections are a great way to cache session specific data. Summarizing the data in this way improves the page's overall performance (each chart fetches 10s of records instead of thousands) while still giving the user real-time data.

This page allows you to search across all Tweets captured by the App. This search capability provides users a great way to search for Tweets focused entirely on APEX. This page utilizes two important APEX Features; Smart Filters and Order By Items.
Smart filters are particularly effective as they combine 'Google Style' row search criteria entry with the ability to present lists or ranges from which a user can select.
In the below screenshot I am selecting tweets for a specific tweeter:

In the screenshot below, I searched for Tweets that contained ckeditor1 and image and applied a filter to only show 'Original' Tweets.

I had a lot of fun building this App. Side projects like these are a great way to flex features that may not get used in your day-to-day corporate development job. They can also benefit the development community. I encourage you to find a side project that gets you excited!