Getting Started

System Overview

How the BizTech app works end-to-end: the two codebases, how they connect, how auth works, how data flows, and the key concepts every developer needs to know.


The Two Codebases

BizTech's application is split across two repositories:

RepositoryRoleStackHosted On
bt-web-v2FrontendNext.js 14, TypeScript, Tailwind, shadcn/ui, Amplify Gen 2Vercel
serverless-biztechapp-1BackendServerless Framework v3, Node.js 20, Lambda, DynamoDBAWS

Both repos should be cloned side-by-side in the same parent directory (e.g. ~/BizTech/).


Request Lifecycle

Every interaction between a user and the backend follows this path:

Browser (Next.js on Vercel)
  |
  |  fetchBackend() or fetchBackendFromServer()
  |  adds Authorization: Bearer <Cognito JWT>
  v
API Gateway (REST API, us-west-2)
  |
  |  Cognito Authorizer validates JWT
  |  (or skips auth for public endpoints)
  v
Lambda Handler (services/{name}/handler.js)
  |
  |  db.getOne(), db.create(), db.scan(), etc.
  |  (lib/db.js auto-appends ENVIRONMENT suffix to table names)
  v
DynamoDB (30+ tables)

Frontend to Backend

The frontend makes API calls through two functions in src/lib/db.ts:

  • fetchBackend() — used client-side in React components and hooks. Gets the auth token from fetchAuthSession() (Amplify).
  • fetchBackendFromServer() — used server-side in getServerSideProps and middleware. Gets the auth token from request cookies via runWithAmplifyServerContext.

Both call the same REST backend. The URL comes from src/lib/dbconfig.ts:

NEXT_PUBLIC_REACT_APP_STAGEAPI URLClient URL
localhttp://localhost:4000http://localhost:3000
productionhttps://api.ubcbiztech.comhttps://app.ubcbiztech.com
anything else (including unset)https://api-dev.ubcbiztech.comhttps://dev.app.ubcbiztech.com

Backend Request Handling

Every Lambda handler follows the same pattern:

  1. Parse input from event.body, event.pathParameters, or event.queryStringParameters
  2. Validate input using helpers.checkPayloadProps() or manual checks
  3. Execute business logic using lib/db.js for DynamoDB access
  4. Return a response using helpers.createResponse(statusCode, body)

The handler file for each service is services/{name}/handler.js. The response helpers are in lib/handlerHelpers.js.


Authentication Model

Authentication uses AWS Cognito via Amplify Gen 2.

How Login Works

  1. User signs in on /login (email/password or Google OAuth)
  2. Cognito returns JWT tokens (id, access, refresh)
  3. Amplify stores tokens in browser cookies (7-day max age)
  4. Every subsequent request includes the JWT in the Authorization header
  5. API Gateway validates the JWT via a Cognito Authorizer before the Lambda runs
  6. The handler reads the user's email from event.requestContext.authorizer.claims.email

Role Model

RoleHow DeterminedAccess
UnauthenticatedNo valid sessionPublic endpoints only (event listing, public profiles)
Non-memberHas Cognito session, isMember = falseRedirected to /membership
MemberisMember = trueAll non-admin pages and endpoints
AdminEmail ends with @ubcbiztech.comAll pages including /admin/*, admin-only endpoints

Middleware

The Next.js middleware (src/middleware.ts) runs on every page request:

  1. Allow-listed paths skip auth entirely (login, signup, companion, events, btx, investments, static assets)
  2. For everything else, fetches the user via GET /users/self
  3. Non-members get redirected to /membership
  4. Non-admins accessing /admin/* get redirected to /
  5. On auth errors, redirects to /login

See Authentication for the full auth flow, Cognito config, and implementation details.


Backend Architecture

The backend is a Serverless Framework monorepo with ~20 microservices. Each service owns a set of API routes and DynamoDB tables.

Service Structure

Every service lives in services/{name}/ and contains:

  • serverless.yml — function definitions, HTTP routes, IAM permissions
  • handler.js — exported Lambda handler functions
  • helpers.js — service-specific business logic (optional)

All services share a single REST API Gateway and Cognito Authorizer, both created by the hello service and imported by all others.

Shared Code

ModulePurpose
lib/db.jsDynamoDB helpers (create, getOne, scan, updateDB, deleteOne, batchGet, batchWrite, query)
lib/handlerHelpers.jsResponse builders (createResponse, inputError, notFoundResponse, etc.)
lib/sesHelper.jsAWS SES email sending
lib/snsHelper.jsSNS notifications (Slack)
lib/search.jsAlgolia search wrapper
constants/tables.jsAll DynamoDB table name constants

Local Development

Running npm start in the backend repo:

  1. Reads sls-multi-gateways.yml for the list of services
  2. Starts each service with sls offline on sequential ports starting at 4001
  3. Runs an Express proxy on port 4000 that routes requests by URL path prefix to the correct service port

You can also run specific services: npm start events users registrations.

See Backend Architecture for the full service list and patterns.


Frontend Architecture

The frontend is a Next.js 14 app using the Pages Router (not App Router). Key structural concepts:

File-Based Routing

Every file in src/pages/ becomes a route. For example:

  • pages/login.tsx/login
  • pages/events.tsx/events
  • pages/admin/event/[eventId]/[year]/index.tsx/admin/event/blueprint/2026

Layout Modes

The app has three layout modes, controlled in _app.tsx:

  1. No layout — full-screen pages (login, signup, membership, btx, investments, live wall)
  2. Companion layout — dark-themed mobile-first event experience (/companion/*)
  3. Standard layout — sidebar navigation + content area (everything else)

Data Fetching

  • SSR with getServerSideProps — for pages that need fresh data on load (home page, profile)
  • Client-side with React Query — for interactive data (events, registrations, connections)
  • Query hooks live in src/queries/ (e.g. useEvents(), useUser(), useRegistrations())

Key Directories

DirectoryWhat's In It
src/components/~155 React components organized by feature area
src/pages/Next.js routes (one file per page)
src/lib/API client, config, utils, query provider
src/queries/TanStack React Query hooks
src/features/Event-specific companion modules (blueprint, kickstart, etc.)
src/constants/App-wide config (companion settings, navigation tabs, etc.)
src/types/TypeScript type definitions
src/util/Utility functions (Amplify server utils, auth helpers, etc.)

See Frontend Architecture for the full breakdown.


Database

All data lives in DynamoDB across 30+ tables. The lib/db.js module handles all access and automatically appends the environment suffix to table names:

  • Dev/local: biztechEvents (no suffix)
  • Production: biztechEventsPROD

Key tables:

TablePrimary KeyWhat It Stores
biztechUsersid (email)User accounts, membership status, admin flag
biztechEventsid + year (sort key)Event definitions with capacity, dates, registration questions
biztechRegistrationsid (email) + eventID;year (sort key)Event registrations with status, responses, check-in data
biztechMembers{year}id (email)Annual membership records
biztechProfilesid (email)Public profiles with skills, social links, visibility settings
biztechTeamsid (team name) + eventID;yearTeams for events (judging, competitions)

See Database for the full table reference.


Environment and Stages

The app runs in three environments:

StageFrontend URLBackend URLDynamoDB Suffix
Locallocalhost:3000localhost:4000(none — uses dev tables)
Devdev.app.ubcbiztech.comapi-dev.ubcbiztech.com(none)
Productionapp.ubcbiztech.comapi.ubcbiztech.comPROD

Dev and Local Share Data

Both dev and local have ENVIRONMENT="", so they read from the same DynamoDB tables. Be careful when testing destructive operations.

The stage is controlled by:

  • Frontend: NEXT_PUBLIC_REACT_APP_STAGE in .env.local
  • Backend: config.{stage}.json files, where the ENVIRONMENT key determines the table suffix

Previous
Introduction