Skip to content

Nasa API with Express and Axios#1

Open
claGiaSW wants to merge 11 commits intomainfrom
query-parameters
Open

Nasa API with Express and Axios#1
claGiaSW wants to merge 11 commits intomainfrom
query-parameters

Conversation

@claGiaSW
Copy link
Copy Markdown
Owner

@claGiaSW claGiaSW commented Nov 12, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Updated README with instructions to sign up for the NASA Open API and create a local .env file.
    • Introduced a new Express application as the main entry point, providing a welcome message and guidance on accessing rover-related endpoints.
    • Added a router for handling requests related to NASA's Mars rover photos, including endpoints for fetching rover lists and photos.
  • Chores

    • Enhanced project configuration in package.json with new metadata and a dependency for environment variable management.
    • Refactored server setup to streamline application initialization and updated the listening port to 3000.

@claGiaSW claGiaSW marked this pull request as ready for review November 12, 2024 10:38
@claGiaSW claGiaSW requested a review from SChandarana November 13, 2024 10:15
@claGiaSW
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 13, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 13, 2024

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Walkthrough

The changes introduce a new Express application structure for a web server, including a router for handling NASA Mars rover photo requests. The README.md file has been updated with instructions for users to sign up for the NASA Open API and create a .env file to store their API key. New dependencies and metadata have been added in package.json, while the server setup in src/server.ts has been refactored to utilize the new app structure.

Changes

File Change Summary
README.md Updated with instructions to sign up for NASA Open API and create a .env file for the API key.
app/index.ts Introduced an Express application that serves as the main entry point, defining routes and exporting the app.
package.json Added new dependency "dotenv": "^16.4.5" and several metadata fields including name, version, description, main entry point, keywords, author, and license.
routes/roverRouter.ts Created a router for NASA Mars rover photos, defining enums and interfaces, and implementing two GET endpoints for fetching rover information and photos.
src/server.ts Refactored to import the app instance, updated the listening port from 5000 to 3000, and removed the initialization of the Express application and middleware setup.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Server
    participant RoverRouter
    participant NASA_API

    User->>Server: GET /
    Server->>RoverRouter: Route request
    RoverRouter->>User: Return welcome message

    User->>Server: GET /rovers
    Server->>RoverRouter: Route request
    RoverRouter->>NASA_API: Fetch available rovers
    NASA_API-->>RoverRouter: Return rover data
    RoverRouter-->>User: Send rover data

    User->>Server: GET /:roverName/photos
    Server->>RoverRouter: Route request
    RoverRouter->>NASA_API: Fetch photos for rover
    NASA_API-->>RoverRouter: Return photo data
    RoverRouter-->>User: Send photo data
Loading

🐰 "In the world of code, a new path we tread,
With rovers and photos, our dreams are widespread.
Sign up for NASA, your key in the air,
Store it in .env, handle it with care.
From ports to routers, our app takes flight,
In the realm of the web, we shine ever bright!" 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (6)
src/server.ts (2)

5-7: Improve logging message for production readiness

The console message includes "Test backend" which might not be appropriate for production. Consider a more generic message or environment-specific logging.

-console.log(`Test backend is running on port ${port}`);
+console.log(`Server is running on port ${port}`);

Also, consider adding error handling for the server startup:

-app.listen(port, () => {
+app.listen(port)
+  .on('error', (error: Error) => {
+    console.error('Failed to start server:', error.message);
+    process.exit(1);
+  })
+  .on('listening', () => {
   console.log(`Server is running on port ${port}`);
-});
+  });

1-7: Consider adding graceful shutdown handling

The server setup should handle process signals for graceful shutdown, especially important for container environments.

Add this after the server startup:

const server = app.listen(port)
  .on('error', (error: Error) => {
    console.error('Failed to start server:', error.message);
    process.exit(1);
  })
  .on('listening', () => {
    console.log(`Server is running on port ${port}`);
  });

process.on('SIGTERM', () => {
  console.log('SIGTERM signal received: closing HTTP server');
  server.close(() => {
    console.log('HTTP server closed');
    process.exit(0);
  });
});
app/index.ts (2)

6-11: Improve root route handler with proper typing and structured response

The root route could be improved with:

  • Proper Express types
  • Structured JSON response following API best practices
  • OpenAPI/Swagger documentation

Consider this improvement:

-app.get("/", (req: any, res: any) => {
+app.get("/", (req: Request, res: Response) => {
+  res.json({
+    message: "Welcome to Mars Rover API",
+    endpoints: {
+      getAllRovers: {
+        path: "/rovers",
+        method: "GET",
+        description: "Get all rovers' information"
+      },
+      getRoverPhotos: {
+        path: "/rovers/:roverName/photos",
+        method: "GET",
+        description: "Get photos from a specific rover"
+      }
+    }
+  });
+});

1-15: Consider adding API service layer and environment validation

For better maintainability and error handling, consider these architectural improvements:

  1. Create a separate service layer for NASA API interactions
  2. Add environment variable validation at startup
  3. Implement request caching to avoid hitting NASA API rate limits
  4. Add request timeout handling

Create a new file services/nasaApi.ts:

import axios from 'axios';
import { validateEnv } from '../utils/validateEnv';

const { NASA_API_KEY } = validateEnv(['NASA_API_KEY']);

const nasaApi = axios.create({
  baseURL: 'https://api.nasa.gov',
  timeout: 5000,
  params: {
    api_key: NASA_API_KEY
  }
});

export const getNasaRoverPhotos = async (roverName: string) => {
  try {
    const response = await nasaApi.get(`/mars-photos/api/v1/rovers/${roverName}/photos`);
    return response.data;
  } catch (error) {
    // Handle specific API errors
    throw error;
  }
};
routes/roverRouter.ts (2)

28-54: Add JSDoc documentation for interfaces

Consider adding JSDoc comments to document the purpose and structure of each interface:

/** Represents a camera mounted on a Mars rover */
interface Camera {
  /** Unique identifier for the camera */
  id?: number;
  // ... etc
}

1-101: Add rate limiting and security headers

Consider adding the following security measures:

  1. Add rate limiting to prevent abuse:
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

roverRouter.use(limiter);
  1. Add security headers using helmet:
import helmet from 'helmet';
roverRouter.use(helmet());
  1. Consider caching responses to reduce API calls to NASA's endpoint:
import apicache from 'apicache';
const cache = apicache.middleware('15 minutes');
roverRouter.use(cache);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 7c5c74d and 76cbf05.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • README.md (1 hunks)
  • app/index.ts (1 hunks)
  • package.json (2 hunks)
  • routes/roverRouter.ts (1 hunks)
  • src/server.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • README.md
  • package.json
🔇 Additional comments (1)
src/server.ts (1)

1-1: Verify the relative import path

The import path "../app" suggests this file is in a subdirectory. Let's verify the project structure to ensure the import path is correct.

✅ Verification successful

Import path "../app" is correctly configured

The relative import path is valid and properly structured:

  • app/index.ts exists at the repository root and exports the Express app correctly
  • src/server.ts is in the src directory, so "../app" correctly references the app module
  • The import pattern is consistent and only used in server.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and location of the app/index.ts file
# Expected: Find the app/index.ts file relative to src/server.ts

# Check if app/index.ts exists at the expected location
fd --type f "index.ts" --exec sh -c '
  if [ "$(dirname "$1")" = "app" ]; then
    echo "Found: $1"
    head -n 5 "$1"  # Show first 5 lines for context
  fi
' sh {}

Length of output: 167


Script:

#!/bin/bash
# Let's try a different approach to verify the import path
# 1. Find the location of server.ts
# 2. Check for app.ts or app/index.ts in nearby directories

# Find server.ts location
echo "=== Server file location ==="
fd "server.ts$"

# List all .ts files in the root and app directories
echo -e "\n=== Searching for app file ==="
fd -e ts -d 2

# Show content of server.ts for context
echo -e "\n=== Server.ts content ==="
if [ -f "src/server.ts" ]; then
    cat "src/server.ts"
fi

Length of output: 494


Script:

#!/bin/bash
# Let's verify the content of app/index.ts to ensure it exports correctly
echo "=== app/index.ts content ==="
cat "app/index.ts"

# Check if there are any other files importing from "../app"
echo -e "\n=== Other files with similar import ==="
rg "from \"\.\./app\"" --type ts

Length of output: 651

…erator || replacing the previous bitwise operator |

- fixed typo in MAHLI enum value
@claGiaSW
Copy link
Copy Markdown
Owner Author

@coderabbitai pause

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (1)
routes/roverRouter.ts (1)

9-54: Enhance type definitions with stricter typing and documentation

The interfaces could benefit from:

  1. Stricter typing for status and dates
  2. Documentation about camera compatibility with rovers
  3. Making required fields non-optional
+/**
+ * Available cameras on Mars rovers
+ * Note: Not all cameras are available on all rovers:
+ * - Curiosity: FHAZ, RHAZ, MAST, CHEMCAM, MAHLI, MARDI, NAVCAM
+ * - Opportunity/Spirit: FHAZ, RHAZ, NAVCAM, PANCAM, MINITES
+ * - Perseverance: FHAZ, RHAZ, NAVCAM
+ */
 enum Cameras {
   // ... existing enum values
 }

+type RoverStatus = 'active' | 'complete' | 'inactive';
+
 interface Camera {
-  id?: number;
+  id: number;
-  rover_id?: number;
+  rover_id: number;
   name: string;
   full_name: string;
 }

 interface Rover {
   id: number;
   name: string;
-  landing_date: string;
-  launch_date: string;
+  landing_date: Date;
+  launch_date: Date;
-  status: string;
+  status: RoverStatus;
   max_sol: number;
-  max_date: string;
+  max_date: Date;
   total_photos: number;
   cameras: Camera[];
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 76cbf05 and fa2687c.

📒 Files selected for processing (1)
  • routes/roverRouter.ts (1 hunks)

Comment on lines +1 to +8
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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:

  1. Group imports by type (external, internal)
  2. Add type definitions for axios
  3. Add comprehensive API validation
+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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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;
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);
});

Comment on lines +71 to +99
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");
})
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

⚠️ Potential issue

Add security measures and update dependencies

The verification reveals several security concerns that need to be addressed:

  • No security headers or rate limiting is currently implemented
  • Missing essential security dependencies (helmet, express-rate-limit)
  • Express version 4.21.1 has known vulnerabilities
  • No input validation/sanitization for URL parameters

Required changes:

  • Update Express to version 4.21.2 or later to patch security vulnerabilities
  • Add helmet and express-rate-limit as dependencies
  • Implement input validation and sanitization as suggested in the review
  • Add security headers through helmet middleware
🔗 Analysis chain

Add rate limiting and security headers

While previous comments covered validation and pagination, additional security measures are needed:

  1. Implement rate limiting to prevent API abuse
  2. Add security headers
  3. Sanitize and validate URL parameters before use
+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 code

Let's verify the security implications:

🏁 Scripts executed

The 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

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 18, 2024

✅ Actions performed

Reviews paused.

Copy link
Copy Markdown
Collaborator

@SChandarana SChandarana left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments :D Some of them may be a touch outdated as there have been some changes since I left them. Feel free to drop any comment responses if anything doesn't make sense!

Comment on lines +6 to +7
dotenv.config({ path: './.env' });
const apiKey = process.env.NASA_API_KEY;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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 +9 to +19
enum Cameras {
FHAZ = "FHAZ",
RHAZ = "RHAZ",
MAST = "MAST",
CHEMCAM = "CHEMCAM",
MAHLI = "MAHLI = 5",
MARDI = "MARDI",
NAVCAM = "NAVCAM",
PANCAM = "PANCAM",
MINITES = "MINITES"
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a wider note, I was speaking about this with Asia. For the case of Cameras and Rovers, we're not actually ever using them by name in the codebase, we're mainly just checking that they conform to a specific set of strings. In this case, we don't need to complicate things with an enum, which has the main purpose of making static values easier to read and digest and can instead set a union of strings as the type i.e. type RoverName: "FHAZ" | "RHAZ" | ... | "MINITES"

This is safer as it uses TypeScripts strict compile time typing, instead of the inbuilt enum stuff in JS (which can have some side effects I believe?)

Times where enums are helpful are when you have a set of a static values that you need to use multiple times in your code and have one source of truth. Common examples are:

  • switch statements
enum Direction {
    Up = "Up",
    Down = "Down",
    Left = "Left",
    Right = "Right"
}

switch (direction: Direction) {
    case Direction.Up:
        // ...
        break;
    case Direction.Down:
        // ...
        break;
    default:
        // ...
        break;
}
  • Attributing named values to weird strings (e.g. colour hex codes)
enum Colours {
    Green: "#00ff00",
    Blue: "#0000ff",
    Red: "#ff0000",
}

setButtonColour(Colours.Red)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants