Money Tracker

Developer

Technical comparison and one-time setup guide for deploying this app.

Technical comparison

How the two integrations differ at the implementation level.

ZapierSheets API
Auth modelNone — webhook URL passed in the request body; no server-side secrets neededOAuth 2.0 Authorization Code Flow — GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET in .env.local; refresh token encrypted (AES-256-GCM) and stored in client localStorage
Server credentialsNoneGOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, ENCRYPTION_KEY (never exposed to the client)
Write pathPOST /api/zapier → validate body → forward to webhook URLPOST /api/sheets → refreshAccessToken() → Google Sheets values.append
Read pathNot implemented — fire-and-forgetGET /api/sheets → refreshAccessToken() → values.get → row parsing
Token handlingN/AServer-side only: POST oauth2.googleapis.com/token with refresh_token; client never receives an access_token
API route size~15 lines — validate + proxy~80 lines — token refresh, API call, error handling, row mapping
Error visibilityZapier task history (external) — app receives only an HTTP 200/error from ZapierStructured Google API errors — typed HTTP 4xx/5xx with messages
Third-party in write pathYes — Zapier; swap the destination (Notion, Airtable, Slack) with zero code changesNo — direct Google API; you own the entire write path
ExtensibilityLimited to what Zapier actions supportFull control — add delete, update, monthly sheet tabs, any Sheets API feature

How the integrations work

Zapier— the app acts as a thin proxy. The client sends the expense payload plus the user's webhook URL to /api/zapier. The route validates the body and forwards it to Zapier. Zapier handles the rest — no Google credentials are ever needed on the server. This is the no-code philosophy: powerful for connecting systems, limited when you need reads or want to avoid per-task pricing.

Sheets API— the app implements the full OAuth 2.0 Authorization Code Flow. The user authorises once via the Google consent screen; the server exchanges the auth code for a refresh token and stores it in the client's localStorage. Every subsequent API call (write or read) calls refreshAccessToken() server-side first — the client never sees an access token. The refresh token persists indefinitely until the user revokes access from their Google account settings.

The OAuth callback route (/api/auth/google/callback) exchanges the code for tokens, fetches the user's email, then redirects to /settings/connect with the refresh token and email as URL params. ConfigForm reads and saves these on mount, then immediately calls history.replaceState to remove them from the URL.

Developer setup

What you need to do once before deploying, per integration.

Zapier — no server setup required

The webhook URL is provided by the user at runtime — it's passed in the request body and never stored server-side. There are no environment variables to set and no Google Cloud configuration needed. Deploy the app and users can start using Zapier immediately.

Sheets API — one-time Google Cloud setup

This setup is done once by the developer. After it's complete, any user can connect their Google account without any further configuration.

1
Go to console.cloud.google.com and create or select a project.
  • APIs & Services → Library → search for Google Sheets API → Enable
2
Create OAuth credentials:
  • APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID
  • Application type: Web application
  • Authorised redirect URIs — add both:
    http://localhost:3000/api/auth/google/callbackhttps://your-production-domain.com/api/auth/google/callback
  • Copy the Client ID and Client Secret
3
Add the credentials to .env.local (local) and your hosting platform (production):
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
ENCRYPTION_KEY=your-32-byte-base64-key
Generate ENCRYPTION_KEY with: openssl rand -base64 32Restart the dev server after saving.
4
If your Google Cloud project is in Testing mode, add test users:
  • OAuth consent screen → Test users → Add users
  • Add the Google accounts that will use the app
  • Publish the app when ready to allow any Google account to connect

Apps in Testing mode can only be authorised by listed test users. Publishing removes this restriction but triggers a Google review for sensitive scopes.

Credentials and security model

GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and ENCRYPTION_KEY live in environment variables and are only used server-side. They are never sent to the client.

After the OAuth flow completes, the server encrypts the refresh token with AES-256-GCM usingENCRYPTION_KEY before sending it to the client. The encrypted blob is stored in localStorage. When the client calls /api/sheets, it sends the blob; the server decrypts it, exchanges it for a short-lived access token, makes the Google Sheets call, and discards both. The access token and plaintext refresh token never reach the client.

The Zapier webhook URL is stored in localStorage and sent in the request body. It is a shared secret — anyone who obtains it can POST to the Zap. The app never logs any credential server-side.