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
-
Planning: Outlined requirements for two endpoints (
/
and/me
), integrated cat facts API, and added features like rate limiting, CORS, and logging. - Setup: Initialized a Node.js project with Express, configured environment variables, and set up dependencies (express, cors, express-rate-limit, dotenv).
-
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.
- Created the main
- Testing: Manually tested endpoints with curl, verified error scenarios, and ensured proper deployment on Vercel.
- 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();
/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" });
}
});
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;
}
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!
Top comments (0)