Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 32 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
# 🚀 Project: Complex NASA API

### Goal: Use NASA's API to return all of their facility locations (~400). Display the name of the facility, its location, and the weather at the facility currently.

### How to submit your code for review:

- Fork and clone this repo
- Create a new branch called answer
- Checkout answer branch
- Push to your fork
- Issue a pull request
- Your pull request description should contain the following:
- (1 to 5 no 3) I completed the challenge
- (1 to 5 no 3) I feel good about my code
- Anything specific on which you want feedback!

Example:
```
I completed the challenge: 5
I feel good about my code: 4
I'm not sure if my constructors are setup cleanly...
```
# NASA and Weather API

> View a list of all of the NASA facilities and the weather at each location.

> <img src="./images/screenshot.png" alt="Screenshot of completed NASA and Weather API App" width="500">

## Table of Contents

1. [Tech Stack](#tech-stack)
1. [Development](#development)
1. [NASA and Weather APIs](#nasa-and-weather-apis)
1. [Notes](#notes)

## Tech Stack

- **HTML**
- **CSS**
- **JavaScript**

## Development

### NASA and Weather APIs

- NASA's Facilities dataset is available at: https://data.nasa.gov/dataset/nasa-facilities-api/resource/8da12948-3793-4ec1-b5c6-f95e86fd6021, no API key required.
- A 3rd party CORS proxy (https://corsproxy.io) was used for the NASA request url to resolve CORS errors.
- Obtain a free Weather API key at: https://www.weatherapi.com/
- Update the `WEATHER_API_KEY` value with your key on line 1 of main.js.
- Open the app in your browser, and view the list of all NASA facilities including the current weather!

### Notes

Visit the official documentation at https://data.nasa.gov/dataset/nasa-facilities-api anf https://www.weatherapi.com/docs/ for more information on API use, copyright, and rate limitations.
Binary file added images/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="View a list of all of the NASA facilities and the weather at each location"
/>
<meta name="keywords" content="nasa, facilities, weather" />
<title>NASA Facilities and Weather</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>NASA Facilities and Weather List</h1>
<section id="container"></section>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
138 changes: 138 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
const WEATHER_API_KEY = ""; // ADD KEY

// NASA may have updated endpoint, now blocked by CORS. Searched for how to bypass CORS without adding a backend/server and found this post: https://www.descope.com/blog/post/cors-errors => then googled cors proxies and found this: https://cors.lol/ and https://corsproxy.io

const nasaURL = `https://corsproxy.io/?url=https://data.nasa.gov/docs/legacy/gvk9-iz74.json`;

fetch(nasaURL)
.then((res) => res.json())
.then((data) => {
// Comment out fetch call and use object below for testing to prevent getting rate limited
// const data = [
// {
// center: "Kennedy Space Center",
// center_search_status: "Public",
// facility: "Control Room 2/1726/HGR-S ",
// occupied: "1957-01-01T00:00:00.000",
// record_date: "1996-03-01T00:00:00.000",
// last_update: "2015-06-22T00:00:00.000",
// country: "US",
// contact: "Sheryl Chaffee",
// phone: "321-867-8047",
// location: {
// latitude: "28.538331",
// longitude: "-81.378879",
// human_address:
// '{"address": "", "city": "", "state": "", "zip": "32899"}',
// },
// city: "Kennedy Space Center",
// state: "FL",
// zipcode: "32899",
// ":@computed_region_bigw_e76g": "173",
// ":@computed_region_cbhk_fwbd": "30",
// ":@computed_region_nnqa_25f4": "1078",
// },
// {
// center: "Langley Research Center",
// center_search_status: "Public",
// facility: "Micometeroid/LDEF Analysis Laboratory",
// occupied: "1965-01-01T00:00:00.000",
// status: "Active",
// record_date: "1996-03-01T00:00:00.000",
// last_update: "2013-02-25T00:00:00.000",
// country: "US",
// contact: "Sherry Johnson",
// phone: "757.864-3848",
// location: {
// latitude: "37.08681",
// longitude: "-76.376649",
// human_address:
// '{"address": "", "city": "", "state": "", "zip": "23681-2199"}',
// },
// city: "Hampton",
// state: "VA",
// zipcode: "23681-2199",
// ":@computed_region_bigw_e76g": "173",
// ":@computed_region_cbhk_fwbd": "40",
// ":@computed_region_nnqa_25f4": "2913",
// },
// ];
data.forEach((place) => {
// Get facility name + location
const { facility, center, city, state, country, zipcode } = place;
const zip = zipcode.split("-")[0];

// Get weather
const weatherURL = `http://api.weatherapi.com/v1/current.json?key=${WEATHER_API_KEY}&q=${zip}`;
fetch(weatherURL)
.then((res) => res.json())
.then((data) => {
// Comment out fetch call and use object below for testing to prevent getting rate limited
// const data = {
// location: {
// name: "Orlando",
// region: "Florida",
// country: "USA",
// lat: 28.3066997528076,
// lon: -80.6862030029297,
// tz_id: "America/New_York",
// localtime_epoch: 1759750427,
// localtime: "2025-10-06 07:33",
// },
// current: {
// last_updated_epoch: 1759750200,
// last_updated: "2025-10-06 07:30",
// temp_c: 26.7,
// temp_f: 80.1,
// is_day: 1,
// condition: {
// text: "Partly Cloudy",
// icon: "//cdn.weatherapi.com/weather/64x64/day/116.png",
// code: 1003,
// },
// wind_mph: 12.5,
// wind_kph: 20.2,
// wind_degree: 93,
// wind_dir: "E",
// pressure_mb: 1014,
// pressure_in: 29.94,
// precip_mm: 0,
// precip_in: 0,
// humidity: 84,
// cloud: 0,
// feelslike_c: 30.4,
// feelslike_f: 86.7,
// windchill_c: 25.3,
// windchill_f: 77.5,
// heatindex_c: 27.9,
// heatindex_f: 82.2,
// dewpoint_c: 22.5,
// dewpoint_f: 72.6,
// vis_km: 16,
// vis_miles: 9,
// uv: 0,
// gust_mph: 24.8,
// gust_kph: 39.9,
// short_rad: 0,
// diff_rad: 0,
// dni: 0,
// gti: 0,
// },
// };
const { condition, temp_f, temp_c } = data.current;

// Add name, location, + weather to the DOM
const section = document.createElement("section");
section.classList.add("card");
section.innerHTML = `
<img src=${"https:" + condition.icon} alt="Weather icon" />
<p>${temp_f}°F (${temp_c}°C)</p>
<p>${facility}</p>
<p>${center}</p>
<p><span>${city}, ${state}, ${zip} ${country}</span></p>
`;
document.getElementById("container").appendChild(section);
});
});
})
.catch((err) => console.error(err));
55 changes: 55 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
* {
box-sizing: border-box;
padding: 0;
margin: 0;
font-weight: unset;
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif;
letter-spacing: 0.4px;
}

:root {
--teal: rgba(38, 102, 133, 0.75);
--black: rgba(51, 51, 51, 1);
--white: rgba(246, 246, 246, 1);
--3d-effect: -webkit-linear-gradient(
top,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0.15)
);
}

body {
max-width: 90vw;
margin: auto;
color: var(--black);
background-color: var(--white);
padding-bottom: 4em;
}

h1 {
background-color: var(--black);
color: var(--white);
width: 100vw;
margin: 0 -5vw 2em;
padding: 1em 5vw;
background-image: var(--3d-effect);
}

#container {
display: flex;
flex-wrap: wrap;
gap: 3em;
justify-content: center;
}

.card {
padding: 1.5em;
border-radius: 0.5em;
overflow: hidden;
background-color: var(--teal);
color: var(--white);
box-shadow: 0 2px 12px grey;
background-image: var(--3d-effect);
max-width: 400px;
width: 350px;
}