Team Burnt Cheese - Kyryl Andreiev, Jarod Atienzo, Dylan Verallo, Jesse Wattenhofer
DEV320 Advanced Web Development • Winter/Spring 2026 • Bellevue College
Most people have no idea how clean or dirty their local power grid is right now. That data exists, but it's buried in APIs and government reports nobody reads. We wanted to put it in front of anyone who types in a city name.
You type a city name into Some Emissions. The app calls the Geocode Maps API to turn that into coordinates, then hits the Electricity Maps API to get the carbon intensity (gCO2/kWh), carbon-free energy percentage, renewable energy percentage, total electricity load, and a full power source breakdown. The result shows up with a simple Low/Moderate/High label so you don't need to know what 95 gCO2/kWh actually means.
The backend runs on NestJS 11 with TypeScript. Controllers take HTTP requests and hand off the actual work to service classes. The frontend is plain HTML5 and JavaScript with Tailwind CSS, served as static files by the same NestJS app over HTTPS.
There are three domain components. CitySearch takes the user's input and validates it. That gets passed to Geocoding, which turns the city name into latitude/longitude via the Geocode Maps API. ElectricityMaps then uses those coordinates to pull carbon intensity, renewable percentage, total load, and the electricity mix. All API keys live server-side in NestJS ConfigService, so nothing sensitive reaches the browser.
Both external APIs (Geocode Maps and Electricity Maps) go
through a single reusable adapter in the CarbonService. One
/api/allCityData endpoint fires off all five
Electricity Maps calls in parallel with
Promise.allSettled, so if one call fails the
rest still come back. We use GitHub for CI/CD and project
tracking. For deployment we use Dokploy, an open-source
self-hostable platform similar to Vercel or Netlify that
runs on our own server. It auto-deploys on every push to
main, and a full build takes about a minute, so the live
site is always running the latest version.
Jesse was team lead, Jarod ran communications, Dylan owned documentation, and I (Kyryl) was the merge manager, meaning every pull request went through me before it hit main. We split the work using a Work Breakdown Structure with four tracks: documentation, API integration, frontend, and CI/CD. By week 8 we had closed over 30 tasks, each with an acceptance review before it counted as done.
Everyone worked on their own branch, opened a PR, and I reviewed and merged it. A GitHub project board showed what was in progress, what was blocked, and what was done. We wrote weekly status reports and ran retrospectives when things felt off. Dylan built the HTML/CSS layout, Jesse handled responsivity and accessibility, Jarod did UI/UX and animations, and I wrote the API logic and data-fetching layer.
Every pull request went through me. I set up the GitHub project board, the branching rules, and managed all merges. I also wrote the API endpoint documentation and data models, drew the sequence diagrams, and led framework and library selection.
For the actual code, I built the energy data integration with the Electricity Maps API, wrote the carbon percentage calculations, and worked on how the data gets displayed on the frontend. I set up the server deployment through Dokploy too, with auto-deploy on push to main.
The SRS has six user stories, use case scenarios with test cases, activity diagrams, class diagrams, ER diagrams, CRC cards, sequence diagrams, and API docs. One decision that shaped the whole app was chaining the Geocode API as a preprocessing step: users just type a city name and the backend handles coordinate lookup on its own.
Here's the user story that drove most of the architecture: "As a user, I want to enter a city name so that I can retrieve carbon intensity data for my region." Most of the request flow came out of that, from the search form through geocoding to the final color-coded intensity display.
One pattern that saved us a lot of repeated code was a
generic API adapter in the CarbonService. Instead of writing
separate fetch logic for each Electricity Maps endpoint, we
wrote one
fetchFromElectricityMaps<T> method. It
takes an endpoint name and coordinates, adds auth headers,
and returns typed data. Adding a new endpoint after that was
a few lines:
private async fetchFromElectricityMaps<T>(
endpoint: string, lat: string, lon: string,
): Promise<T> {
const apiKey = this.configService.get<string>('ELECTRICITYMAPS_API_KEY');
const url = `https://api.electricitymaps.com/v3/${endpoint}?lat=${lat}&lon=${lon}`;
const response = await firstValueFrom(
this.httpsService.get<T>(url, { headers: { 'auth-token': apiKey } }),
);
return response.data;
}
Our GitHub project board tracked every task from backlog to done. During retrospectives it made it easy to see where things were stuck.
We didn't get to everything we originally planned. Local storage is next so users can save favorite cities without re-searching every time. After that we want a history view that shows how a region's carbon intensity changes over time, and a comparison mode for viewing two cities side-by-side.
The full system vision in our SRS also includes historical
carbon intensity tracking, transportation emissions
calculations (car, bus, train, etc.), and personalized
recommendations for reducing your footprint. Those were
always out of scope for this quarter but they're where the
project would go next. On the backend, we'd like to add
domain-level exception classes like
CityNotFound and an adapter layer so the
services aren't tied to the exact shape of the external API
responses.
Being merge manager meant reading everyone's code every week, which forced me to understand parts of the app I didn't write. I got faster at spotting issues in a diff before they hit main. I also wrote the API docs before any backend code existed. At the time it felt like busywork, but when I actually sat down to implement the endpoints I already knew the data shapes and error cases. CI/CD went in during week one, so broken builds showed up in minutes. Dokploy went in around the same time. Once it was wired to main, every merge went live in about a minute with no manual steps.
This is the first time I've built software with a team where everyone had a real role and real ownership. Coordinating schedules was harder than I expected, but watching four separate pieces come together into something that actually worked made it worth the friction.
Before this project, requirements analysis and design patterns were things I'd only read about. Now I've actually written an SRS that drove real implementation decisions. The backend and infrastructure skills I picked up (NestJS, TypeScript, CI/CD, API design) are the kind of work I want to do after graduation.
I'm better at process and organization than I thought. Setting up the GitHub board, the CI pipeline, the branching rules, that came naturally. The hardest moment was early on when the Electricity Maps API returned data in a different structure than their docs described. I kept trying to force my code to match the docs until I finally just logged the raw responses. The actual fix took five minutes after that.
Advice for anyone starting something similar: write your API contracts first, set up CI/CD on day one, pick a merge workflow early, and talk to your team more than you think you need to.
Some Emissions is live at some-emissions.karilaa.dev. Source code is on GitHub.
Type a city name into the search field (try "Seattle") and press Enter. The app looks up the coordinates, pulls the latest grid data, and shows you the carbon intensity in gCO2/kWh with a color-coded Low/Moderate/High label, the renewable energy percentage, and a breakdown of where the power is coming from. No account needed.