From Frontend to Backend: Building My First REST API π

Just a regular guy who wants to make an impact in the society
Introduction
As a frontend developer comfortable with React and Next.js, I recently joined the HNG internship backend track that pushed me out of my comfort zone. The first task? Build a RESTful API endpoint that integrates with an external API and returns dynamic data.
Spoiler alert: It was both challenging and enriching! Here's everything I learned building my first backend service from scratch.
π The Challenge
The task was straightforward yet comprehensive:
Build a GET /me endpoint that:
Returns my profile information in JSON format
Fetches a random cat fact from an external API on every request
Includes a dynamic timestamp in ISO 8601 format
Handles errors gracefully with appropriate fallbacks
Implements rate limiting for security
Deploys to a production environment (no Vercel or Render allowed!)
Required Response Format:
{
"status": "success",
"user": {
"email": "your.email@example.com",
"name": "Your Full Name",
"stack": "Node.js/Express"
},
"timestamp": "2025-10-19T14:30:45.123Z",
"fact": "Cats can rotate their ears 180 degrees."
}
π€ My Initial Thoughts
Coming from frontend development, I was familiar with consuming APIs but had never built one myself. Questions flooded my mind:
How do I structure a backend project?
What's the difference between middleware and route handlers?
How do I handle errors when external APIs fail?
What even is rate limiting, and how do I implement it?
Time to dive in! πͺ
π οΈ Tech Stack Selection
I chose Node.js with Express because:
JavaScript familiarity - I already knew JS from React
Quick setup - Express is minimal and straightforward
Rich ecosystem - Tons of packages available via npm
Great learning curve - Perfect for backend beginners
Dependencies Used:
express- Web framework for routing and middlewareaxios- HTTP client for external API callsdotenv- Environment variable managementnodemon- Development server with auto-reload
ποΈ Building the API - Step by Step
Step 1: Project Setup
mkdir hng-stage-0-backend
cd hng-stage-0-backend
npm init -y
npm install express axios dotenv
npm install --save-dev nodemon
Simple enough! Coming from React, this felt similar to create-react-app, just more manual.
Step 2: Understanding Express Basics
This was my biggest "aha!" moment. In React, I write components. In Express, I write routes and middleware.
React Component (what I knew):
function Profile() {
return <div>Profile Data</div>;
}
Express Route (what I learned):
app.get('/me', (req, res) => {
res.json({ data: 'Profile Data' });
});
Both handle requests and return data - just different environments!
Step 3: Creating the Main Endpoint
Here's the core implementation:
app.get('/me', async (req, res) => {
try {
// Fetch cat fact from external API
const catFact = await fetchCatFact();
// Generate current timestamp
const timestamp = new Date().toISOString();
// Build response
const response = {
status: 'success',
user: {
email: 'your.email@example.com',
name: 'Your Full Name',
stack: 'Node.js/Express'
},
timestamp: timestamp,
fact: catFact
};
res.status(200).json(response);
} catch (error) {
console.error('Error:', error);
res.status(500).json({
status: 'error',
message: 'Internal server error'
});
}
});
Key Learning: Always wrap async operations in try-catch blocks for safety
Step 4: Integrating External API
This part felt familiar just like fetching data in React, but on the server side:
async function fetchCatFact() {
try {
const response = await axios.get('https://catfact.ninja/fact', {
timeout: 5000,
headers: { 'Accept': 'application/json' }
});
return response.data.fact;
} catch (error) {
console.error('Cat API failed:', error.message);
// Fallback if external API is down
return 'Cats sleep 70% of their lives. (Fallback - API unavailable)';
}
}
Lesson Learned: Always have a fallback! External APIs can fail, and your app should handle it gracefully.
Step 5: Implementing Rate Limiting
This was completely new territory. Rate limiting prevents abuse by limiting how many requests a user can make.
const rateLimit = new Map();
const MAX_REQUESTS = 30; // per minute
const RATE_LIMIT_WINDOW = 60000; // 1 minute
const rateLimiter = (req, res, next) => {
const ip = req.ip;
const now = Date.now();
if (!rateLimit.has(ip)) {
rateLimit.set(ip, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });
return next();
}
const record = rateLimit.get(ip);
if (now > record.resetTime) {
record.count = 1;
record.resetTime = now + RATE_LIMIT_WINDOW;
return next();
}
if (record.count >= MAX_REQUESTS) {
return res.status(429).json({
status: 'error',
message: 'Too many requests'
});
}
record.count++;
next();
};
// Apply to route
app.get('/me', rateLimiter, async (req, res) => { ... });
Mind Blown: Middleware is just a function that runs before your route handler! It's like a React Context or HOC, but for the backend.
π’ Deployment Journey
The Challenge
No Vercel or Render allowed so I chose Railway for its simplicity and generous free tier.
Deployment Steps:
Pushed to GitHub
Connected Railway to my GitHub repo
Set environment variables in Railway dashboard
Generated public domain
Tested the live endpoint π
Live URL: hng-stage-0-backend-production-9495.up.railway.app
Deployment Gotcha
Initially, my app crashed because I hardcoded PORT=3000. Railway uses dynamic ports!
Fix:
const PORT = process.env.PORT || 3000;
Always respect environment variables in production! π
π Challenges I Faced
Challenge 1: Understanding Middleware
Problem: I didn't understand the order of middleware execution.
Solution: Middleware runs top-to-bottom! Think of it as a pipeline:
Request β CORS β Logging β Rate Limiter β Route Handler β Response
Challenge 2: Handling External API Failures
Problem: What if the Cat Facts API is down?
Solution: Always implement fallbacks with try-catch blocks and provide default data.
Challenge 3: CORS Errors
Problem: When testing from a frontend, I got CORS errors.
Solution: Added CORS headers:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
Challenge 4: Git Tracking node_modules
Problem: Accidentally committed node_modules/ to GitHub.
Solution:
git rm -r --cached node_modules
git commit -m "Remove node_modules"
Lesson: Create .gitignore BEFORE your first commit!
π‘ Key Takeaways
1. Backend β Hard, Just Different
The concepts aren't harder than frontend - they're just different. Once I mapped them to what I knew (routes = components, middleware = HOCs), everything clicked.
2. Error Handling is Critical
On the frontend, a failed fetch might just show an error message. On the backend, poor error handling can crash your entire server! Always use try-catch.
3. Environment Variables Are Your Friend
Never hardcode sensitive data or configuration. Use .env locally and set variables in your deployment platform.
4. Testing Matters
I tested my endpoint with:
curl commands
Browser
Postman
Multiple networks
Each revealed different issues!
5. Documentation is Gold
Writing a clear README helped me understand my own code better and made deployment smoother.
πΈ Screenshots & Results
Local Testing:
$ curl http://localhost:3000/me
{
"status": "success",
"user": {
"email": "yourname@example.com",
"name": "Your Name",
"stack": "Node.js/Express"
},
"timestamp": "2025-10-19T14:30:45.789Z",
"fact": "Cats have 32 muscles in each ear."
}
Production Testing:
$ curl hng-stage-0-backend-production-9495.up.railway.app
β
200 OK
β
All fields present
β
Dynamic timestamp
β
Fresh cat fact on each request
Rate Limiting Test: After 30 requests in a minute:
{
"status": "error",
"message": "Too many requests. Please try again later."
}
π― What's Next?
This task opened my eyes to backend development! Next steps on my learning journey:
Database Integration - Add MongoDB/PostgreSQL
Authentication - JWT tokens and user sessions
Advanced Error Handling - Custom error classes
Testing - Write unit and integration tests with Jest
WebSockets - Real-time features
Microservices - Break into smaller services
π Resources That Helped
π Links
Live API: hng-stage-0-backend-production-9495.up.railway.app
GitHub Repo: https://github.com/Vixs101/hng-stage-0-backend
Cat Facts API: https://catfact.ninja/fact
π Final Thoughts
Going from frontend to backend felt like learning to cook after years of food delivery. Both get you fed, but understanding what happens "in the kitchen" gives you so much more control and appreciation for the full stack.
To any frontend devs hesitant about backend: Just start! You already know more than you think. JavaScript is JavaScript, whether it's running in the browser or on a server. The patterns are similar, just the environment is different.
This was Stage 0 of my backend journey, and I can't wait to see where it goes! π
π Let's Connect
Found this helpful? Have questions? Let's chat!
Twitter/X: @vixs101
GitHub: Elijah Victor
Drop your backend learning experiences in the comments below! π
#Backend #NodeJS #API #WebDevelopment #LearningInPublic #BackendDevelopment #JavaScript #ExpressJS #100DaysOfCode

