DEV Community

Cover image for Building a Dynamic Cat Facts API with Node.js and Express
Birusha Ndegeya
Birusha Ndegeya

Posted on

Building a Dynamic Cat Facts API with Node.js and Express

Introduction

In this HNG Stage Zero backend task, I built a simple RESTful API using Node.js and Express that serves user profile information combined with dynamic cat facts from an external API. The goal was to demonstrate API integration, error handling, and best practices in backend development.

Work Process

  1. Planning: Outlined requirements for two endpoints (/ and /me), integrated cat facts API, and added features like rate limiting, CORS, and logging.
  2. Setup: Initialized a Node.js project with Express, configured environment variables, and set up dependencies (express, cors, express-rate-limit, dotenv).
  3. Implementation:
    • Created the main index.js with Express app, middleware, and routes.
    • Built a utility function get-fact.js for fetching cat facts with timeout and error handling.
    • Added request logging and structured JSON responses.
  4. Testing: Manually tested endpoints with curl, verified error scenarios, and ensured proper deployment on Vercel.
  5. Deployment: Configured for Vercel serverless functions and added Docker support.

What I Learned

  • API Integration: Handling external APIs with proper error handling, timeouts, and fallbacks.
  • Middleware Usage: Implementing rate limiting and CORS for security and usability.
  • Environment Management: Using dotenv for configuration in different environments.
  • Serverless Deployment: Adapting code for Vercel with export default and conditional local server.
  • Best Practices: Structured code, JSDoc comments, and comprehensive README documentation.

Code Snippets

Main App Setup

import express from "express";
import cors from "cors";
import rateLimit from "express-rate-limit";
import dotenv from "dotenv";
import { getFact } from "./utils/get-fact.js";

const app = express();
app.use(cors());
const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100 });
app.use(limiter);
dotenv.config();
Enter fullscreen mode Exit fullscreen mode

/me Endpoint

app.get("/me", async function (req, res) {
  try {
    const fact = await getFact(process.env.FUN_FACT_API || 'https://catfact.ninja');
    res.json({
      status: "success",
      user: { email: "birushandegeya@gmail.com", name: "Birusha Ndegeya", stack: "Node.js/Express" },
      timestamp: new Date().toISOString(),
      fact: fact,
    });
  } catch (error) {
    res.status(500).json({ status: "error", message: "Failed to fetch cat fact" });
  }
});
Enter fullscreen mode Exit fullscreen mode

Utility Function

export async function getFact(baseURL) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 5000);
  const response = await fetch(`${baseURL}/fact`, { signal: controller.signal });
  clearTimeout(timeoutId);
  if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
  const data = await response.json();
  return data.fact;
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

This project reinforced my skills in building scalable APIs and integrating third-party services. The experience highlighted the importance of robust error handling and security measures in backend development. Check out the full code on GitHub and try the live API!

hng #nodejs #express #api #backend #hng-stage-zero

Top comments (0)