The Problem
Pharmacy Decoder is a single-file web app. It serves 340 UMPJE practice questions to pharmacy students. Schools get access codes that unlock 14 days of free use. Students verify their school email via a one-time password sent to their inbox.
That OTP flow needs server-side logic. You cannot send emails from the browser. You cannot verify codes securely in client-side JavaScript. The question was: what is the simplest possible backend that handles this without introducing a build system, a deployment pipeline, or a monthly hosting bill?
Why Google Apps Script
Google Apps Script is JavaScript that runs on Google's infrastructure. It can send emails via Gmail, read and write to Google Sheets, and expose web endpoints via the doGet and doPost functions. It is free for the usage levels a small app needs.
For Pharmacy Decoder, Apps Script handles three things:
First, OTP generation and delivery. When a student enters their school email and access code, the app calls Apps Script. The script generates a 6-digit code, stores it with an expiration timestamp, and sends it to the student's inbox via Gmail.
Second, OTP verification. The student enters the code. The app calls Apps Script again. The script checks the code against the stored value and expiration. If valid, it returns success.
Third, activation logging. On successful verification, the script writes a row to a Google Sheets tab: full name, email, access code, graduation year, exam date, follow-up date, activation timestamp.
The JSONP Pattern
Apps Script endpoints do not support CORS in the way a typical API does. The standard fetch API with no-cors mode cannot read the response body. The solution is JSONP: dynamic script tag injection.
The app creates a script element, sets the src to the Apps Script URL with a callback parameter, and appends it to the document. Apps Script wraps the response in a function call that matches the callback name. When the script loads, the browser executes the callback with the response data.
This pattern is old and unfashionable. It also works perfectly for this use case with zero configuration.
What This Costs
Nothing. Google Apps Script has generous daily quotas for email sending (100 emails per day on a free account, 1500 on Google Workspace) and script execution time. Pharmacy Decoder does not come close to any of these limits.
Compare this to a Vercel serverless function, which would work but introduces a build step, a deployment pipeline, environment variables, and a framework dependency. For what Pharmacy Decoder needs, that is unnecessary complexity.
When Apps Script Is the Wrong Choice
Apps Script is wrong when you need real-time performance, complex routing, or heavy computation. It is wrong when you need WebSocket connections or streaming responses. It is wrong when your app grows to hundreds of concurrent users.
For a pharmacy exam prep app that serves a few hundred students per school cohort with occasional OTP verification requests, it is the laziest correct solution. And in software, the laziest correct solution is usually the best one.
The Takeaway
Not every app needs a full backend. Google Apps Script gives you server-side email sending, data storage via Sheets, and web endpoints for free. For small, focused apps where the backend logic is simple, it is a tool worth knowing.
