This content originally appeared on DEV Community and was authored by Bouhadjila Hamza
Retry, Abort, and Batch like a Pro — Build a Resilient Express API with oh-no-again.js
Are you tired of flaky APIs ruining your vibe? Let’s fix that with a fun Express.js project using the free and awesome Rick and Morty API and a utility package built from pain and frustration: oh-no-again
.
Retry failed requests\
Batch concurrent fetches\
Timeout and abort support\
All powered by native
fetch
What Are We Building?
An Express.js API endpoint:
GET /characters?ids=1,2,3
It will:
- Fetch multiple Rick and Morty characters in parallel
- Retry failed requests automatically with exponential backoff
- Abort slow requests with a timeout
- Report success and failure clearly
Step 1: Setup the Project
mkdir rickmorty-batch-api
cd rickmorty-batch-api
npm init -y
npm install express oh-no-again
Step 2: Create the API
Create a file called server.js
:
const express = require('express');
const { requestBatcher } = require('oh-no-again');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
app.get('/characters', async (req, res) => {
try {
const ids = (req.query.ids || '')
.split(',')
.map((id) => parseInt(id.trim()))
.filter(Boolean);
if (!ids.length) {
return res
.status(400)
.json({ error: 'Please provide character IDs: ?ids=1,2,3' });
}
const result = await requestBatcher(
ids,
3, // concurrency
(id) => ({
url: `https://rickandmortyapi.com/api/character/${id}`,
method: 'GET',
headers: { 'Content-Type': 'application/json' },
}),
{
retries: 3,
delay: 200,
timeout: 100,
returnMeta: true,
hooks: {
onRetry: (err, attempt) =>
console.warn(`[RETRY] Attempt ${attempt + 1}: ${err.message}`),
onFailure: (err) => console.error('[FAILURE]', err.message),
onSuccess: (res) =>
console.log('[SUCCESS] Character fetched.', res.name),
},
},
);
return res.json({ count: result.length, result });
} catch (err) {
res.status(500).json({
error: 'Failed to fetch users',
message: err.message,
});
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Step 3: Test It
Start the server:
node server.js
Then visit:
http://localhost:3000/characters?ids=1,2,3,9999
You’ll get a result like:
{
"count": 4,
"result": [
{
"item": 1,
"result": { "id": 1, "name": "Rick Sanchez", ... },
"success": true
},
{
"item": 2,
"result": { "id": 2, "name": "Morty Smith", ... },
"success": true
},
...
{
"item": 9999,
"error": "Fetch failed with 404 Not Found. Body: {}",
"success": false
}
]
}
Under the Hood — What is oh-no-again
?
oh-no-again
is a lightweight utility that gives you:
Feature | Description |
---|---|
retryHelper |
Retries any async function with AbortSignal support |
requestBatcher |
Handles concurrency, batching, retries and metadata |
Built-in fetch | Uses native fetch, no axios dependency |
Hook system |
onRetry , onFailure , onSuccess , onBatchComplete
|
API Recap
requestBatcher(data, concurrency, taskFn, options)
taskFn: (item) => {
url: string,
method?: string,
headers?: Record<string, string>,
body?: any
}
Options include:
-
retries
,delay
,timeout
-
returnMeta
: true returns{ success, result, item }
-
failFast
: true throws error immediately on any failure -
hooks
: lifecycle tracking
Final Thoughts
This mini project demonstrates how retry logic and batching can be elegant, safe, and fun.\
No more callback hell. No more flaky APIs ruining your night.
And thanks to the open Rick and Morty API — we had a blast doing it.
Resources
Rick and Morty API: https://rickandmortyapi.com
oh-no-again
: https://www.npmjs.com/package/oh-no-againGitHub Repo: github.com/yupher/oh-no-again
Built by Bouhadjila Hamza
Because we’ve all said: “oh no… not again.”
This content originally appeared on DEV Community and was authored by Bouhadjila Hamza