While Heroku is a popular platform for NodeJS developers to deploy mini-scale projects, I haven’t encountered any practical or in-depth resolution to the ❝H12 Request Timeout❞ issue, which leads me to share my recent approach in handling this error. For starters, it is important to identify the root cause of this error — that being Heroku’s web server is set to automatically terminate a HTTP web request (usually an API GET/POST) when time taken to process the API response exceeds 30 seconds.
Assuming that the API server is up and running, this narrows the list of possible approaches down to solely reducing the amount of time taken by each API call to under 30 seconds (keyword being ❛each❜).
Use-Case: My Bus Route Visualisation Site
To better understand the travelling behaviour of public transportation commuters, I was tasked a few months ago to perform an analysis which involves trip chaining a series of bus journeys. As with most Automated Fare Collection systems, the bus journeys from Origin to Destination were inferred from the passengers’ boarding & alighting information. Hence, this led me to develop a bus route visualisation site which enables the selection of Origin — Destination to assist in my upcoming bus route analysis:

While the data provider has included spatial coordinates and bus stop sequences in its public API, considering that there are over ~25K of unique bus routes in Singapore and each API response call returns at most 500 records, I had to implement data concatenation on my end in order to render all embedded bus route information on first load.
Note: The API provided follows an Offset Pagination
format:

Attempt №1: Implemented 100% Client-Side Pagination
With reference to an excellent article on Client Side vs Server Side pagination, Client Side Pagination means that when a query is made, the server returns to the client all the data in one big chunk.
Since all available bus services had to be retrieved on first load for user selection, the decision to implement Client-side pagination was intuitive:

However, I quickly realised upon deployment that the time taken for the 3 primary API calls I had to make – (1) Bus Stops; (2) Bus Services; and (3) Bus Routes on the Heroku web server was vastly greater than the time taken on my localhost.
⚠ This is particularly true for API call (3) Bus Routes, where over 25K of records were initially retrieved from the data provider, easily exceeding the 30 seconds timeout rule which triggered the H12 timeout error‼ ☹
Attempt №2: Implemented 100% Server-Side Pagination
With reference to Client Side vs Server Side pagination, Server Side Pagination is when the server in which the data is hosted only returns a subset of the data requested by the Client.
Since returning all records of (3) Bus Routes in a single aggregated array is not feasible due to server timeout, I proceeded to implement Server-Side pagination instead where multiple Ajax requests were done from the frontend. Each Ajax request retrieves 500 records from the data provider and all aggregation code logic has been shifted over to the browser side instead:
While the H12 error was finally resolved, the user experience was compromised because the initial load was disappointingly slow 😐
Attempt №3: Implemented Hybrid (Client+Server) Pagination
To reduce the no. of calls made from the frontend, instead of returning 500 records directly from the backend to the frontend for aggregation, I attempted to strike a balance between client and server data request volume. Eventually, I decided to aggregate each API request of 500 records into multiple arrays in the server (app.js) code where each array contains 5000 records and is returned to the frontend to process:
Boosting Overall Performance with Redis Caching
While I have finally managed to balance the no. of API requests sent on both server and client-side, in order to further improve user experience, I decided to store the API results via Redis Caching such that after the initial load, subsequent visits by the same user would enable the same API results to be retrieved via the Redis instance and minimise loading time. To integrate Redis caching, it is first necessary to set up a Redis account (stores up to 30MB for free-tier version) at https://redislabs.com/. For a detailed step-by-step walkthrough to set up a Redis account, an in-depth article written by Ramki Pitchala is available over at Host and Use Redis for Free — DEV Community 🙃
After retrieving the Redis credentials, the Redis Node package is installed via npm install redis
and imported into the server file (app.js). The required Redis credentials are specified in a .env
file which is by default included in the .gitignore
file. ⚠ This is extremely important as these credentials are confidential and should not be exposed as plain text for others to steal and exploit the developer’s account. This rule applies to other tokens such as Google API Key or other API access passwords. To retrieve the credentials via the file, dotenv Node package should also be installed and included in the server file as require("dotenv").config()
. Minimally, the .env
should include the following in order to connect to the Redis instance successfully:
REDIS_USERNAME=<email linked to Redis account>
REDIS_PASSWORD=<password token linked to Redis account>
REDIS_ENDPOINT_URI=<Redis instance endpoint provided>
REDIS_DB=<name used to refer to the Redis instance>
Considering the occasional changes to Singapore’s bus routes, I decided to set the Redis in-memory storage to expire in 60 days (~2 months). Before deploying it to its production environment on the Heroku server, the Redis credentials must be configured over at Heroku’s server config page (since the .env file was meant for testing in a development setting):

And yay! My web app was successfully deployed onto Heroku without the H12 timeout error:

Small note on rationale of building this app: While there are other bus route visualisation sites available, I couldn’t for the life of me find any export functionality present for the user to retrieve the data of specific bus routes for custom Origin – Destination pairings. Oh wells…
FYI: The full source code can be found at my GitHub repo and the app is currently deployed at: https://sg-transportation.herokuapp.com/
Update: Due to changes in Heroku’s web hosting platform, application is no longer deployed on Heroku. Apologies for any inconvenience.
Many thanks for reading and please follow me if you would like to read my other upcoming recounts of tackling challenges at work (both people and technical issues)! Would really appreciate it 😀