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) ⚛️

Frontend of BirthdayMails app.


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 📧

example birthday emails - sent from BirthdayMails
Sample automated welcome and birthday emails sent from the BirthdayMails app.
Screenshot showing two previously run workflows
Screenshot showing logs from Render.com (Node.js back-end hosting)

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.

screenshot showing BirthdayMails API User Dashboard
Screenshot showing BirthdayMails API User Dashboard

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