<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tobechukwu Ejiofor </title>
    <description>The latest articles on DEV Community by Tobechukwu Ejiofor  (@etobe).</description>
    <link>https://dev.to/etobe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1653441%2Faf00affd-7041-42fb-980e-0aed8e9dfb9d.png</url>
      <title>DEV Community: Tobechukwu Ejiofor </title>
      <link>https://dev.to/etobe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/etobe"/>
    <language>en</language>
    <item>
      <title>Building the "shadcn/ui for Backend": A Contract-First Approach</title>
      <dc:creator>Tobechukwu Ejiofor </dc:creator>
      <pubDate>Sat, 24 Oct 2026 08:26:11 +0000</pubDate>
      <link>https://dev.to/etobe/building-the-shadcnui-for-backend-a-contract-first-approach-1ili</link>
      <guid>https://dev.to/etobe/building-the-shadcnui-for-backend-a-contract-first-approach-1ili</guid>
      <description>&lt;p&gt;Hi everyone! I'm Tobechukwu. I'm currently building an open-source project called Plughub. It's a CLI and registry for pluggable backend services. &lt;/p&gt;

&lt;p&gt;The idea is simple: instead of adopting a massive BaaS (like Supabase or Firebase), you should be able to pick and choose backend capabilities. You run a command like &lt;code&gt;backkit add email&lt;/code&gt;, and it generates the actual provider code (SMTP, Resend, etc.) directly into your project.&lt;/p&gt;

&lt;p&gt;Here is what I learned while building the Email and Storage capabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Transport Lock-in
&lt;/h3&gt;

&lt;p&gt;Recently, I was testing the SMTP provider for the email capability. I tried to send a real email, but my mobile carrier actively blocks outbound connections on port 587 (a common anti-spam measure). &lt;/p&gt;

&lt;p&gt;I couldn't test my own code. And it made me realize a bigger problem: what happens if a user of my CLI is on an enterprise network that blocks SMTP? Are they just stuck?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Transport-Agnostic Contracts
&lt;/h3&gt;

&lt;p&gt;I decided to build an HTTP-based provider (Resend) that sends emails via port 443. But the real magic was how I structured the architecture to allow this.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;capability&lt;/strong&gt; defines the contract: &lt;code&gt;sendEmail(input) -&amp;gt; result&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The user's &lt;strong&gt;application code&lt;/strong&gt; only imports this contract.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;provider&lt;/strong&gt; (SMTP or Resend) handles the actual transport.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I swapped the SMTP provider for the Resend provider, the application code didn't change. It just kept calling &lt;code&gt;email.sendEmail()&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Same Applies to Storage
&lt;/h3&gt;

&lt;p&gt;I applied the exact same logic to build a Storage capability. Using the AWS SDK v3, the &lt;code&gt;s3&lt;/code&gt; provider can connect to AWS, Cloudflare R2, or a local MinIO instance. &lt;/p&gt;

&lt;p&gt;The app doesn't care where the file goes; it just calls &lt;code&gt;storage.uploadFile()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;p&gt;I'm currently polishing the Developer Experience (auto-installing dependencies, generating &lt;code&gt;.env&lt;/code&gt; files) before I make the repository public. &lt;/p&gt;

&lt;p&gt;If you're interested in the "shadcn/ui for backend" concept, follow me here or on X (@TAGdev278) to see when the repo goes live!&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
