DEV Community

Cover image for FlowHub: I got tired of rewriting SMS/OAuth/webhook code every time we switched providers, so I split it into 16 npm packages and one all-in-one package
Aman Bhakar
Aman Bhakar

Posted on

FlowHub: I got tired of rewriting SMS/OAuth/webhook code every time we switched providers, so I split it into 16 npm packages and one all-in-one package

Every backend I've worked on ends up with the same mess: Twilio calls in one file, Stripe-style OAuth in another, S3 upload logic somewhere else — each with its own error shape, its own (usually missing) retry logic, and no consistent way to know "this thing happened" across the app. Swapping Twilio for MSG91 means hunting down every call site.

So I built Flow Hub — 16 small, independently-installable @flowhub/* packages that put one typed interface in front of each concern (SMS, email, OAuth, storage, OTP, webhooks, queue, cache), backed by a shared event bus. Provider swaps become a register() call, not a refactor.

Before / after

Hand-rolled multi-provider SMS with retry + fallback — the code most teams actually ship:

\ts
async function sendOtpSms(to: string, body: string) {
for (const attempt of [1, 2, 3]) {
try {
await twilioClient.messages.create({ to, body, from: TWILIO_FROM });
return;
} catch {
if (attempt === 3) {
await msg91Client.send({ to, body }); // different call shape
return;
}
await new Promise((r) => setTimeout(r, 200 * attempt));
}
}
}
\
\

With Flow Hub:

\`ts
import { SmsRegistry } from "@flowhub/sms";
import { retry } from "@flowhub/retry";

const sms = new SmsRegistry(app.events);
sms.register(twilioProvider);
sms.register(msg91Provider);

async function sendOtpSms(to: string, body: string) {
try {
await retry(() => sms.send("twilio", to, body), { attempts: 3, delayMs: 200, backoff: "exponential" });
} catch {
await sms.send("msg91", to, body);
}
}
`\

Same behavior, and app.events.on("sms.sent", ...) / sms.failed fire regardless of which provider handled it.

Try it in 60 seconds

Real terminal output, against what's actually published:

\`console
$ npm add -g @flowhub/cli
$ flowkit init my-app
create my-app/package.json
create my-app/index.js
create my-app/.gitignore

$ cd my-app && npm install && flowkit add twilio
update package.json (+@flowhub/sms)

$ npm install && npm start
FlowKit app ready
`\

What's in it

  • core — event bus, DI, plugin lifecycle
  • sms / email / oauth / storage / webhook / queue / cache — provider registries, each with typed register() + events
  • otp — generation/verification/expiry/cooldown
  • express / cli — route helpers and the flowkit scaffolding CLI
  • flowhub — a meta-package that re-exports all of the above in one npm install if you don't want to add them one at a time

All MIT-licensed, all on npm under @flowhub, source at github.com/gitaman69/flowhub.

The part I want to be honest about

Right after publishing, I went back and installed all 16 packages fresh from the registry (not the local workspace) and wrote a smoke test hitting every module's real behavior. Found a real bug: @flowhub/otp's resend-cooldown was tracked on the same object verify() deletes on success — so a successful verify silently reset the cooldown, letting generate()verify()generate() bypass the rate limit meant to throttle SMS/email sends. Fixed, tested, republished as 0.0.3. Change log's in the repo if you want the details.

Feedback, bug reports, and provider implementations (got MSG91, real GCS, real Mailgun?) welcome — that's what the CONTRIBUTING guide is for.

Top comments (4)

Collapse
 
fibonachi profile image
Opstan

Publishing the OTP cooldown bug and explaining how it was fixed is exactly the kind of transparency that builds confidence in an infrastructure library.

As FlowHub grows across multiple packages and providers, developers may need a reliable history of security-sensitive fixes, API guarantees and migration decisions.

Have you considered keeping an append-only public release journal where every important disclosure and commitment remains permanently available, even after the repository documentation is reorganized or rewritten?

Collapse
 
aman_bhakar_289b2bef4b7c5 profile image
Aman Bhakar

Thanks for the suggestion! We already maintain a CHANGELOG.md where we keep track of every significant change, including fixes, improvements, and release updates. We'll continue to use it to ensure a transparent and consistent project history.

Collapse
 
fibonachi profile image
Opstan

Thanks, that makes sense. A well-maintained CHANGELOG.md is already a strong foundation for transparency.

Would the FlowHub team be open to us publishing a short introduction to the project on Opstan, using only information and links from your official repository?

Opstan is a decentralized Proof-of-Work social network and permanent public record platform. Publications are timestamped inside the blockchain and cannot later be edited or deleted. This would give FlowHub an additional immutable record of its development, help more people discover the project, and strengthen its long-term reputation through independently verifiable history.

We can send you the text before publication to ensure everything is accurate. Would you be comfortable with us publishing it?

Thread Thread
 
aman_bhakar_289b2bef4b7c5 profile image
Aman Bhakar

Hi, thanks for reaching out and for considering FlowHub. We appreciate your interest in the project. We're open to the idea, provided the publication is based solely on information from our official repository and accurately represents the project. Please feel free to share the draft with us before publishing so we can review it for factual accuracy.