-
Notifications
You must be signed in to change notification settings - Fork 0
Nasa API with Express and Axios #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8b72661
542603a
73442db
d2ddfef
4177236
3dfa493
8a254ec
a40437f
c2c5a7d
76cbf05
fa2687c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,5 @@ | ||
| Run project with | ||
| `npm run start` | ||
|
|
||
| Sign up to the NASA Open API to get your personal key. | ||
| Create a `.env` local file and store your new key there to use this app. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import express from "express"; | ||
| import roverRouter from "../routes/roverRouter"; | ||
|
|
||
| const app = express(); | ||
|
|
||
| app.get("/", (req: any, res: any) => { | ||
| res.send(`Hello space traveller! | ||
| What are you exploring today? | ||
| A) /rovers = all rovers' info | ||
| B) /rovers/[your preferred rover's name]/photos = photos from that rover`) | ||
| }); | ||
|
|
||
| app.use("/rovers", roverRouter); | ||
claGiaSW marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export default app; | ||
claGiaSW marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,101 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import express from "express"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import axios from "axios"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const roverRouter = express.Router(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import dotenv from "dotenv"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dotenv.config({ path: './.env' }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const apiKey = process.env.NASA_API_KEY; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+7
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code rabbit suggestion about checking if the API key exists is quite useful. Often times a missing environment variable is frustrating as you may think something is broken with the endpoint you've created, when in fact its just that you're missing a variable! Adding in a small check to ensure its not empty is good for that and someone can fix it easily. If you start getting more and more environment variables, that's when you would potentially want a separate file that loads and validates env vars (then stores them in an object) :D |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve imports organization and add API validation While the previous review covered Express types, there are additional improvements needed:
+import { AxiosError, AxiosResponse } from 'axios';
import axios from "axios";
import express from "express";
+import { Router, Request, Response } from "express";
import dotenv from "dotenv";
dotenv.config({ path: './.env' });
const apiKey = process.env.NASA_API_KEY;
+
+// Validate NASA API connectivity early
+async function validateApiKey() {
+ try {
+ await axios.get('https://api.nasa.gov/mars-photos/api/v1/rovers', {
+ params: { api_key: apiKey }
+ });
+ } catch (error) {
+ const axiosError = error as AxiosError;
+ if (axiosError.response?.status === 403) {
+ throw new Error('Invalid NASA_API_KEY');
+ }
+ throw new Error('Failed to validate NASA API connection');
+ }
+}
+
+validateApiKey().catch(error => {
+ console.error(error);
+ process.exit(1);
+});📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enum Cameras { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FHAZ = "FHAZ", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| RHAZ = "RHAZ", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MAST = "MAST", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CHEMCAM = "CHEMCAM", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MAHLI = "MAHLI", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MARDI = "MARDI", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NAVCAM = "NAVCAM", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PANCAM = "PANCAM", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MINITES = "MINITES" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| enum Rovers { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CURIOSITY = "Curiosity", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SPIRIT = "Spirit", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OPPORTUNITY = "Opportunity", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PERSEVERANCE = "Perseverance" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface Camera { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rover_id?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| full_name: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface Rover { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| landing_date: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| launch_date: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_sol: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| max_date: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| total_photos: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cameras: Camera[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface Photo { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sol: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| camera: Camera; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| img_src: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| earth_date: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rover: Rover; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| roverRouter.get('/', (req: any, res: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| axios.get(`https://api.nasa.gov/mars-photos/api/v1/rovers`, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_key: apiKey | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .then(response => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.send(response.data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .catch(error => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.render("error"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
claGiaSW marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| roverRouter.get('/:roverName/photos', (req: any, res: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { roverName } = req.params; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { camera, sol, page, paginationStart, paginationEnd } = req.query; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| axios.get(`https://api.nasa.gov/mars-photos/api/v1/rovers/${roverName}/photos`, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| api_key: apiKey, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| camera: camera, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| page: page || 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sol: sol || 1000 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .then(response => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const photos: Photo[] = response.data.photos; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const urls: string[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| photos.forEach(photo => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| urls.push(photo.img_src); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.send(urls.slice(paginationStart - 1, paginationEnd)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .catch(error => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| res.render("error"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+71
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Add security measures and update dependencies The verification reveals several security concerns that need to be addressed:
Required changes:
🔗 Analysis chainAdd rate limiting and security headers While previous comments covered validation and pagination, additional security measures are needed:
+import rateLimit from 'express-rate-limit';
+import helmet from 'helmet';
+
+// Apply rate limiting
+const limiter = rateLimit({
+ windowMs: 15 * 60 * 1000, // 15 minutes
+ max: 100 // limit each IP to 100 requests per windowMs
+});
+
+roverRouter.use(limiter);
+roverRouter.use(helmet());
+
roverRouter.get('/:roverName/photos', (req: any, res: any) => {
+ // Sanitize URL parameters
+ const roverName = encodeURIComponent(req.params.roverName);
+ if (!Object.values(Rovers).includes(roverName as Rovers)) {
+ return res.status(400).json({ error: 'Invalid rover name' });
+ }
- const { roverName } = req.params;
// ... rest of the codeLet's verify the security implications: 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check for potential security issues in dependencies
gh api graphql -f query='
{
securityVulnerabilities(first: 5, ecosystem: NPM, package: "express") {
nodes {
advisory {
summary
severity
publishedAt
}
vulnerableVersionRange
}
}
}'
# Check if rate limiting is implemented elsewhere
rg -l "rateLimit|express-rate-limit"
Length of output: 1203 Script: #!/bin/bash
# Check if security headers or rate limiting is already implemented in the main app setup
rg -A 5 "app\.(use|set)\("
# Check for existing security-related dependencies in package files
fd "package.json" --exec cat {}
# Check for any existing input validation or sanitization
ast-grep --pattern 'req.params.$_'
Length of output: 659 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default roverRouter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,7 @@ | ||
| import express from "express"; | ||
| import app from "../app"; | ||
|
|
||
| const app = express(); | ||
| const port = 5000; | ||
|
|
||
| app.use(express.json()); | ||
| const router = express.Router(); | ||
|
|
||
| router.get('/', (req: any, res: any) => res.send('Hello world !')); | ||
| app.use('/', router); | ||
| const port = 3000; | ||
claGiaSW marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| app.listen(port, () => { | ||
| console.log(`Test backend is running on port ${port}`); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.