Some Emissions

Team Burnt Cheese - Kyryl Andreiev, Jarod Atienzo, Dylan Verallo, Jesse Wattenhofer

DEV320 Advanced Web Development • Winter/Spring 2026 • Bellevue College

Live App GitHub Repository

The problem

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.

Our solution

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.

Technical architecture

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.

How we worked

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.

My contributions

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.

Documentation highlights

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.

What comes next

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.

My experience

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.

The app

Some Emissions is live at some-emissions.karilaa.dev. Source code is on GitHub.

How to use

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.

Back to Home