A full-stack online banking platform built with Next.js 15, Appwrite, Plaid, and Dwolla. Users can sign up, securely link real bank accounts, view balances and transactions, and transfer funds between accounts.
- 🔐 Authentication — Email/password sign-up and sign-in via Appwrite, with rate limiting on repeated failed attempts
- 🏦 Bank linking — Connect real bank accounts through Plaid Link, with a Dwolla customer created for money movement
- 💵 Dashboard — View linked accounts, total balances, and recent transactions at a glance
- 📊 Transaction history — Paginated, categorized transaction list per account
- 💸 Funds transfer — Send money between linked accounts using Dwolla transfers
- 🧾 My Banks — Manage and view all connected bank accounts
- 🐛 Error monitoring — Sentry integration for client, server, and edge runtimes
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router, Turbopack) |
| Language | TypeScript |
| Styling | Tailwind CSS + shadcn/ui (Radix primitives) |
| Auth & Database | Appwrite |
| Bank connections | Plaid |
| Money movement | Dwolla |
| Forms & validation | React Hook Form + Zod |
| Charts | Chart.js / react-chartjs-2 |
| Monitoring | Sentry |
npm installCopy .env.example to .env and fill in your credentials:
cp .env.example .env# NEXT
NEXT_PUBLIC_SITE_URL=http://localhost:3000
# APPWRITE
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT=
APPWRITE_DATABASE_ID=
APPWRITE_USER_COLLECTION_ID=
APPWRITE_ITEM_COLLECTION_ID=
APPWRITE_BANK_COLLECTION_ID=
APPWRITE_TRANSACTION_COLLECTION_ID=
NEXT_APPWRITE_KEY=
# PLAID
PLAID_CLIENT_ID=
PLAID_SECRET=
PLAID_ENV=
PLAID_PRODUCTS=
PLAID_COUNTRY_CODES=
# DWOLLA
DWOLLA_KEY=
DWOLLA_SECRET=
DWOLLA_BASE_URL=
DWOLLA_ENV=.env.sentry-build-plugin (separate file, already gitignored):
SENTRY_AUTH_TOKEN=- Appwrite: create a project and database with
user,item/bank, andtransactioncollections; generate an API key forNEXT_APPWRITE_KEY. - Plaid: use
sandboxcredentials from the Plaid Dashboard for local development; test bank logins use Plaid's sandbox credentials (e.g.user_good/pass_good). - Dwolla: use a sandbox account for local development. Note that Dwolla's sandbox is US-only — customers require a real, valid 5-digit ZIP code and a 2-letter state code, or account creation will fail with a
ValidationError. - Sentry: the only required credential is
SENTRY_AUTH_TOKEN(Settings → Auth Tokens, needsproject:releasesscope), used at build time to upload source maps. It belongs in.env.sentry-build-plugin, not.env. The DSN and org/project slug aren't secrets, but make sure they point to your own Sentry project rather than a leftover from a tutorial/template.
npm run devOpen http://localhost:3000 to see the app.
app/
(auth)/ # Sign-in / sign-up routes and layout
(root)/ # Authenticated app: dashboard, my-banks, transaction-history, payment-transfer
api/ # API routes (e.g. Sentry example)
components/
AuthForm.tsx # Sign-in/sign-up form with Zod validation
CustomInput.tsx # Reusable form field (label, input, optional helper description, error message)
PlaidLink.tsx # Plaid Link button + bank-linking flow, with loading state during token exchange
ui/ # shadcn/ui primitives
lib/
actions/ # Server actions (user.actions.ts, dwolla.actions.ts, bank.actions.ts, transaction.actions.ts, etc.)
appwrite.ts # Appwrite client setup
plaid.ts # Plaid client setup
rate-limit.ts # Simple in-memory rate limiting for sign-in attempts
utils.ts # Shared helpers + authFormSchema (Zod validation for auth forms)
types/ # Shared TypeScript types
constants/ # App-wide constants (sidebar links, categories, etc.)
Sign-up
- Check whether the email is already registered with Dwolla (Dwolla customers can only be suspended, never deleted, so a previously-used email is permanently blocked).
- Create the Appwrite auth account.
- Create a Dwolla customer using the submitted personal/address details.
- Save the user document in Appwrite (Dwolla customer ID/URL included).
- If any step after account creation fails, the Appwrite account is rolled back so the email isn't left in a stuck, half-created state.
Bank linking
- Request a Plaid Link token for the signed-up user.
- Open Plaid Link; on success, exchange the public token for an access token and create a Dwolla funding source.
- Redirect to the dashboard once linking completes.
- Dwolla postal codes: must be a real 5-digit US ZIP (optionally ZIP+4, e.g.
10001-1234). This is enforced client-side via thepostalCoderegex inauthFormSchema(lib/utils.ts). - Dwolla state codes: must be a valid 2-letter US state abbreviation (e.g.
NY), also enforced client-side. - Sandbox emails: once a Dwolla sandbox customer is created (even if later abandoned), that email can't be reused — sign-up checks for this up front via
checkDwollaCustomerEmailExists. - Sentry credentials: only
SENTRY_AUTH_TOKEN(in.env.sentry-build-plugin) needs to be a real secret you generate yourself. The DSN and org/project slug are public-safe identifiers, but if you're starting from a cloned template, double check they point to your own Sentry project and not the original author's.
The easiest way to deploy this app is via the Vercel Platform. Make sure all environment variables above are configured in your Vercel project settings, and that DWOLLA_ENV/PLAID_ENV are switched to production when you're ready to go live.