Automated Birthday Wishes API Documentation
Created by: Suresh Shrestha
Last updated: March 2, 2025
How It Works?
1️⃣ User data is collected via a frontend application (React in my case) ⚛️

2️⃣ The backend (Node.js & Express) processes the data and stores user details (first name, last name (optional), birth date, email, phone) in MongoDB 🔑
3️⃣ A cron job runs daily (via GitHub Actions) to check for birthdays ⏰
4️⃣ The backend fetches birthday users and sends emails automatically using Nodemailer 📧



Overview
This API provides user authentication, API key management, and user management functionalities. Users can sign up, log in, retrieve their API key, change passwords, regenerate their API key, and manage user information.
Tech Stack
Backend: Node.js, Express, MongoDB
Frontend: React, Tailwind CSS
Automation: Cron jobs via GitHub Actions
Messaging: Emails via Nodemailer, and SMS via Twilio
All Free Resources Used:
✅ MongoDB Atlas – Free database hosting
✅ Render.com – Free Node.js backend hosting (https://bday-787u.onrender.com)
✅ Netlify – Free React frontend hosting (https://birthdaymails.netlify.app, BirthdayMails API Console)
✅ GitHub Actions – Free CI/CD & cron job scheduler
✅ Nodemailer (Gmail SMTP) – Free email sending
I planned to enable SMS via Twilio, but since it’s a paid service, I haven’t activated it yet.
Security:
Authentication
The API uses API Key authentication. Users receive an API key upon signup, which they can use for future authentication. API key is passed in the request headers.
const authHeader = req.header("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({ error: "API key is required" });
}
The API key is sent in the authorization header using the Bearer scheme.
Example request:
GET /users/all
Authorization: Bearer YOUR_API_KEY
Authorization
The API uses role-based authorization, ensuring only authorized users can access certain features.
Database Connection
The API uses MongoDB as its database. Connection is established via the connectDB function:
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI);
console.log('✅ MongoDB Connected');
} catch (error) {
console.error('❌ MongoDB connection error:', error);
process.exit(1);
}
};
module.exports = connectDB;
In my database, I have two collections:
1. apiusers Collection
- This stores details about user who interact with my API. It includes these fields: _id, email(string), password(string), apikey(string), role(string)
2. users Collection
- This stores general user information. It inculcates these fields: _id, firstName(string), lastName(string), birthdate(date), email(string), phone(string), subscribed(boolean)
API User Routes & Their Functions:
|
SN |
Routes |
Function |
|---|---|---|
|
1 |
POST /api/auth/signup |
Registers a new user and generates an API key. |
|
2 |
POST /api/auth/login |
Logs in a user and returns their API key. |
|
3 |
PUT /api/auth/change-password |
Allows users to change their password. |
|
4 |
PUT /api/auth/regenerate-key |
Allows users to regenerate a new API key. |
|
5 |
POST /api/auth/get-key |
Allows users to retrieve their API key if forgotten. |
|
6 |
DELETE /api/auth/delete |
Deletes a user’s account. |

1. API User Signup
Endpoint: POST /api/auth/signup
Request Body:
{
"email": "user@example.com",
"password": "secure*Password54"
}
Success Response:
{
"message": "API Key generated",
"apiKey": "generated-api-key"
}
2. API User Login
Endpoint: POST /api/auth/login
Request Body:
{
"email": "user@example.com",
"password": "secure*Password54"
}
Success Response:
{
"message": "Login successful",
"apiKey": "user-api-key"
}
3. Change API User Password
Endpoint: PUT /api/auth/change-password
Request Body:
{
"email": "user@example.com",
"currentPassword": "oldpassword",
"newPassword": "newsecurepassword"
}
Success Response:
{
"message": "Password changed successfully"
}
4. Regenerate API Key
Endpoint: PUT /api/auth/regenerate-key
Request Body:
{
"email": "user@example.com",
"password": "secure*Password54"
}
Success Response:
{
"message": "New API Key generated",
"apiKey": "new-api-key"
}
5. Retrieve API Key
Endpoint: POST /api/auth/get-key
Request Body:
{
"email": "user@example.com",
"password": "secure*Password54"
}
Success Response:
{
"apiKey": "user-api-key"
}
6. Delete API Key
Endpoint: DELETE /api/auth/delete
Request Body:
{
"email": "user@example.com",
"password": "secure*Password54"
}
Success Response:
{
"message": "API user deleted successfully"
}
Birthday Users API Routes & Their Functions:
|
SN |
Routes |
Function |
|---|---|---|
|
1 |
POST /users/add |
Add a user with first name, last name, email, birthday, email, phone |
|
2 |
PUT /users/update/:id |
Update user details |
|
3 |
DELETE /users/delete/:id |
Remove a user |
|
4 |
GET /users/all |
Retrieve all users |
|
5 |
PUT /users/subscribe/:id |
Subscribe user to email and SMS |
|
6 |
PUT /users/unsubscribe/:id |
Unsubscribe user from email and SMS |
1. Add User
Endpoint: POST /users/add
Request Body:
{
"firstName": "John",
"lastName": "Doe",
"birthdate": "2000-12-25",
"email": "john@example.com",
"phone": "1234567890",
"recaptchaToken": "recaptcha-token"
}
Success Response:
{
"message": "User added successfully!"
}
2. Update User
Endpoint: PUT /users/update/:id
Request Body:
{
"firstName": "Jane",
"lastName": "Doe",
"birthdate": "2002-01-01",
"email": "jane@example.com",
"phone": "9876543210"
}
Success Response:
{
"message": "User updated successfully",
"user": { "updated-user-data" }
}
3. Delete User
Endpoint: DELETE /users/delete/:id
Success Response:
{
"message": "User deleted successfully"
}
4. Get All Users
Endpoint: GET /users/all
Success Response:
[
{
"_id": "67b96317e47490f6d728e00d",
"firstName": "John",
"lastName": "Doe",
"birthdate": "2000-12-25",
"email": "john@example.com",
"phone": "+1234567890",
"subscribed": true,
"createdAt": "2025-02-22T05:22:38.275Z"
},
{
"_id": "67b96317e47420f4d728e00f",
"firstName": "Jane",
"lastName": "Doe",
"birthdate": "2002-01-01",
"email": "jane@example.com",
"phone": "+9876543210",
"subscribed": false,
"createdAt": "2025-02-24T05:22:38.275Z"
},
]
5. Subscribe User to Email & SMS
Endpoint: PUT /users/subscribe/:id
Success Response:
{
"message": "User subscribed successfully"
}
6. Unsubscribe User from Email & SMS
Endpoint: Get /users/unsubscribe/:id
Endpoint: PUT /users/unsubscribe/:id
This does not return JSON but rather updates the subscription status and redirects, the response to a PUT request will be:
If the user is found and unsubscribed:
HTTP 302 (Redirect)
Location: <REDIRECT_URL>/unsubscribe?status=success&id=<user_id>
If the user is not found:
HTTP 302 (Redirect)
Location: <REDIRECT_URL>/unsubscribe?status=not_found
If there is an error (e.g., database issue):
HTTP 302 (Redirect)
Location: <REDIRECT_URL>/unsubscribe?status=error
Security: API Key & Role-Based Authorization
Every API request requires an API key for authentication.
I wanted to ensure that only the super admin has permission to view all users, remove a user, and update existing user information. Since birthday user data includes sensitive personal details like birth-dates and phone numbers, I implemented these restrictions to protect user privacy and prevent unauthorized access.
|
Route |
Requires Auth? |
Allowed Roles |
|---|---|---|
|
POST /users/add |
✅ Yes |
admin, superadmin |
|
PUT /users/update/:id |
✅ Yes |
superadmin |
|
DELETE /users/delete/:id |
✅ Yes |
superadmin |
|
GET /users/all |
✅ Yes |
superadmin |
|
PUT /users/subscribe/:id |
✅ Yes |
admin, superadmin |
|
PUT /users/unsubscribe/:id |
✅ Yes |
admin, superadmin |
|
GET /users/unsubscribe/:id |
❌ No |
Public |
