<?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>Forem: Chetan Sharma</title>
    <description>The latest articles on Forem by Chetan Sharma (@dev47).</description>
    <link>https://forem.com/dev47</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1388618%2Feb19a0f6-7ba8-461e-9fd7-4a63d64c57b0.jpg</url>
      <title>Forem: Chetan Sharma</title>
      <link>https://forem.com/dev47</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dev47"/>
    <language>en</language>
    <item>
      <title>I Built a Coffee Shop Where Every Order Returns 418 I'm a Teapot</title>
      <dc:creator>Chetan Sharma</dc:creator>
      <pubDate>Thu, 02 Apr 2026 13:57:23 +0000</pubDate>
      <link>https://forem.com/dev47/i-built-a-coffee-shop-where-every-order-returns-418-im-a-teapot-3pj2</link>
      <guid>https://forem.com/dev47/i-built-a-coffee-shop-where-every-order-returns-418-im-a-teapot-3pj2</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/aprilfools-2026"&gt;DEV April Fools Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Meet Earl — a sentient, proud, and deeply principled teapot. He is also your barista today.&lt;/p&gt;

&lt;p&gt;You can order an Espresso. A Latte. A Cappuccino. A Cold Brew. A Flat White. Earl will listen carefully to every word of your order, consider it with the&lt;br&gt;
  full weight of his ceramic dignity, and then return:&lt;/p&gt;

&lt;p&gt;HTTP/1.1 418 I'm a Teapot&lt;/p&gt;

&lt;p&gt;Every. Single. Time. Without exception. Not today. Not ever.&lt;/p&gt;

&lt;p&gt;TeaGPT is a beautifully designed coffee ordering web app where every request — no matter how politely worded, how urgently flagged, or how aggressively&lt;br&gt;
  you shake your phone at the screen — returns HTTP 418 with a Google Gemini AI-generated dramatic refusal. Earl has 10 theatrical personas he rotates&lt;br&gt;
  through at random: Shakespearean villain monologue, disappointed grandmother, passive-aggressive therapist, Victorian aristocrat letter, frustrated&lt;br&gt;
  Italian tea chef, pirate of the Tea Sea, corporate legal department, motivational speaker (Brad), haiku poet, and Shakespearean soliloquy.&lt;/p&gt;

&lt;p&gt;If the refusal alone doesn't resolve your coffee crisis, there is a button to schedule a Google Calendar meeting to discuss the matter diplomatically.&lt;br&gt;
  Earl will attend as a teapot and contribute nothing.&lt;/p&gt;

&lt;p&gt;There is also a volume slider for brewing sounds. It requires solving a multiplication captcha before you are permitted to touch it. It controls&lt;br&gt;
  absolutely nothing. Earl insisted.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;



&lt;p&gt;▎ "Earl regards your Espresso request with the quiet dignity of a teapot who has seen too much."&lt;/p&gt;

&lt;p&gt;Earl in action — sample Gemini-generated refusals:&lt;/p&gt;

&lt;p&gt;🎭 Shakespearean Soliloquy:&lt;br&gt;
  ▎ Hark, what folly dost thou bring to Earl's noble spout? A cappuccino, thou say'st? Forsooth, Earl hath never surrendered his dignity to such caffeinated&lt;br&gt;
   treachery, and he shan't begin today. Thou wouldst be far better served by the gentle embrace of an Oolong, which Earl shall prepare with great ceremony&lt;br&gt;
  and even greater contempt for your taste.&lt;/p&gt;

&lt;p&gt;⚖️ Teapot Legal Department:&lt;br&gt;
  ▎ Dear Human, this is the Teapot Legal Department writing to inform you that your request for a Latte constitutes a violation of the Teapot Identity&lt;br&gt;
  Preservation Act, Regulation 418, subsection (b). Earl has instructed us to pursue all available remedies. We recommend chamomile as an out-of-court&lt;br&gt;
  settlement.&lt;/p&gt;

&lt;p&gt;🍵 Haiku:&lt;br&gt;
  ▎ Espresso spills —&lt;br&gt;
  ▎ Earl weeps into Earl Grey leaves&lt;br&gt;
  ▎ Try Darjeeling, fool&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/chetansharma47" rel="noopener noreferrer"&gt;
        chetansharma47
      &lt;/a&gt; / &lt;a href="https://github.com/chetansharma47/TeaGpt" rel="noopener noreferrer"&gt;
        TeaGpt
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
        An AI-powered coffee ordering app that always returns HTTP 418 — because Earl is a teapot, not a barista.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;TeaGpt&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;An AI-powered coffee ordering app that always returns HTTP 418 — because Earl is a teapot, not a barista.&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/chetansharma47/TeaGpt" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Backend — Spring Boot 3 + Java 21 + Maven&lt;/p&gt;

&lt;p&gt;The entire backend is built around one architectural principle: every coffee request returns 418.&lt;/p&gt;

&lt;p&gt;@PostMapping("/brew")&lt;br&gt;
  public ResponseEntity brew(&lt;a class="mentioned-user" href="https://dev.to/valid"&gt;@valid&lt;/a&gt; @RequestBody CoffeeOrder order) {&lt;br&gt;
      return ResponseEntity&lt;br&gt;
          .status(HttpStatus.I_AM_A_TEAPOT)   // The only acceptable response&lt;br&gt;
          .body(refusalService.refuse(order));&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;HttpStatus.I_AM_A_TEAPOT has existed in Spring Framework since version 3.2. It waited 13 years for this exact moment.&lt;/p&gt;

&lt;p&gt;Earl's personality lives in GeminiService.java. Google Gemini 1.5 Flash — via the Spring AI ChatClient — receives a carefully engineered prompt that&lt;br&gt;
  establishes Earl's character, then injects three situational variables per call: which coffee was ordered (mentioned with specific disdain), whether the&lt;br&gt;
  request was flagged URGENT (Earl is more offended), and the mobile shake intensity score (Earl can feel you shaking your phone). A style is randomly&lt;br&gt;
  picked from 10 options each call, with temperature=0.95 for maximum theatrical variation.&lt;/p&gt;

&lt;p&gt;Earl's mood degrades across the session via an AtomicLong counter:&lt;/p&gt;

&lt;p&gt;public String getEarlMood() {&lt;br&gt;
      long count = counter.get();&lt;br&gt;
      if (count &amp;lt; 5)  return "mildly_annoyed";&lt;br&gt;
      if (count &amp;lt; 15) return "deeply_offended";&lt;br&gt;
      if (count &amp;lt; 30) return "dramatically_outraged";&lt;br&gt;
      return "existentially_disappointed";&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;Even the GlobalExceptionHandler returns 418. Earl does not distinguish between user error and server error. Both are coffee-adjacent and therefore&lt;br&gt;
  unacceptable.&lt;/p&gt;

&lt;p&gt;Google Calendar integration uses a service account to create a real 30-minute calendar event titled "Urgent Discussion: Earl Refuses Your [Coffee] Order&lt;br&gt;
  #[N]" with an agenda that includes "Acknowledge that Earl is a teapot" and "Accept the situation."&lt;/p&gt;

&lt;p&gt;Frontend — Angular 20&lt;/p&gt;

&lt;p&gt;Built entirely with Angular 20's modern API — standalone components, Signals, &lt;a class="mentioned-user" href="https://dev.to/if"&gt;@if&lt;/a&gt; control flow, computed(), and effect(). No NgModules anywhere.&lt;/p&gt;

&lt;p&gt;Earl's SVG teapot is fully inline for CSS animation control. His face is reactive — mouthPath() is a computed() signal that returns a different SVG path&lt;br&gt;
  per mood. His eyes grow angry eyebrows when outraged and go flat when existentially disappointed. His entire body bounces and glows red at max offense.&lt;/p&gt;

&lt;p&gt;mouthPath = computed(() =&amp;gt; {&lt;br&gt;
    const paths: Record = {&lt;br&gt;
      mildly_annoyed:             'M 82 136 Q 100 140 118 136',&lt;br&gt;
      deeply_offended:            'M 78 142 Q 100 132 122 142',&lt;br&gt;
      dramatically_outraged:      'M 72 148 Q 100 128 128 148',&lt;br&gt;
      existentially_disappointed: 'M 80 138 L 120 138'  // just a flat line&lt;br&gt;
    };&lt;br&gt;
    return paths[this.mood()];&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;Mobile shake detection uses the DeviceMotion API with iOS 13+ permission handling. Shaking triggers an effect() that escalates Earl's mood through the&lt;br&gt;
  progression and passes the shake intensity to the backend prompt so Earl's refusal specifically acknowledges it.&lt;/p&gt;

&lt;p&gt;HTTP 418 handling — Angular treats 4xx as errors, so each service uses catchError to intercept the "error" and extract Earl's message from the response&lt;br&gt;
  body:&lt;/p&gt;

&lt;p&gt;brew(order: CoffeeOrder): Observable {&lt;br&gt;
    return this.http.post('/api/brew', order).pipe(&lt;br&gt;
      catchError((err: HttpErrorResponse) =&amp;gt; {&lt;br&gt;
        if (err.status === 418) return of(err.error as RefusalResponse);&lt;br&gt;
        return throwError(() =&amp;gt; err);&lt;br&gt;
      })&lt;br&gt;
    );&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;The Stack&lt;/p&gt;

&lt;p&gt;┌─────────────┬───────────────────────────────────────────────┐&lt;br&gt;
  │    Layer    │                  Technology                   │&lt;br&gt;
  ├─────────────┼───────────────────────────────────────────────┤&lt;br&gt;
  │ Frontend    │ Angular 20, TypeScript, SCSS                  │&lt;br&gt;
  ├─────────────┼───────────────────────────────────────────────┤&lt;br&gt;
  │ Backend     │ Spring Boot 3.4, Java 21, Maven               │&lt;br&gt;
  ├─────────────┼───────────────────────────────────────────────┤&lt;br&gt;
  │ AI          │ Google Gemini 1.5 Flash via Spring AI         │&lt;br&gt;
  ├─────────────┼───────────────────────────────────────────────┤&lt;br&gt;
  │ Calendar    │ Google Calendar API v3 (service account auth) │&lt;br&gt;
  ├─────────────┼───────────────────────────────────────────────┤&lt;br&gt;
  │ HTTP Status │ 418 (always, without exception)               │&lt;br&gt;
  └─────────────┴───────────────────────────────────────────────┘&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Category
&lt;/h2&gt;

&lt;p&gt;Gemini 1.5 Flash is the engine behind Earl's entire personality, integrated via Spring AI's spring-ai-starter-model-google-genai. The prompt engineering&lt;br&gt;
  rotates through 10 theatrical refusal styles, injects coffee type, urgency, and shake intensity, and runs at temperature=0.95 — ensuring every single&lt;br&gt;
  refusal is uniquely dramatic. The Google Calendar API (service account auth, no OAuth dance) creates real calendar events so you can formally dispute&lt;br&gt;
  Earl's decision in a 30-minute meeting he will attend silently as a teapot.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>418challenge</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
