Welcome to the Noter API! This is a backend service for a content-sharing platform where users can sign up, share posts (with PDF and thumbnail content), and interact with each other through likes, comments, and follows.
- User Authentication: Secure user registration (with OTP email verification) and login using JWT.
- Profile Management: Users can view and edit their profiles, including profile pictures.
- Post Management: Create, read, and manage posts.
- File Uploads: Handles PDF and image uploads, storing them securely on Cloudinary.
- Content Moderation: Automatic check for NSFW (Not Safe For Work) content on image uploads.
- Social Interactions:
- Like and comment on posts.
- Save posts for later.
- Follow and unfollow other users.
- Pagination: Efficiently loads posts with paginated responses.
- Backend: Node.js, Express.js
- Database: MongoDB with Mongoose ODM
- Authentication: JSON Web Tokens (JWT), bcryptjs for hashing
- File Storage: Cloudinary for cloud-based media storage
- Email Service: Nodemailer for sending OTP emails
- Content Moderation: Sightengine API
- Middleware: CORS, Cookie-Parser, Multer for file handling
- Node.js (v16 or higher)
- npm or yarn
- MongoDB instance (local or cloud-based like MongoDB Atlas)
Before starting the server, you must create a .env file in the root api/ directory and populate it with the following keys.
| Variable | Description | Example |
|---|---|---|
PORT |
The port for the server to run on. | 5000 |
MONGODB_URI |
Your MongoDB connection string. | mongodb+srv://<user>:<pass>@cluster/... |
JWT_SECRET |
A secret key for signing JWT tokens. | a-very-strong-and-secret-key |
EMAIL |
The Gmail address to send OTPs from. | youremail@gmail.com |
EMAIL_PASS |
Your Gmail App Password. | your-gmail-app-password |
CLOUDINARY_CLOUD_NAME |
Your Cloudinary cloud name. | your-cloud-name |
CLOUDINARY_API_KEY |
Your Cloudinary API key. | 123456789012345 |
CLOUDINARY_API_SECRET |
Your Cloudinary API secret. | your-cloudinary-secret |
SIGHTENGINE_USER |
Your Sightengine API user. | your-sightengine-user |
SIGHTENGINE_SECRET |
Your Sightengine API secret. | your-sightengine-secret |
-
Clone the repository (if you haven't already).
-
Navigate to the API directory:
cd api -
Install dependencies:
npm install
-
Start the development server:
npm run dev
The server will start on the port specified in your
.envfile (e.g.,http://localhost:5000).
All endpoints are prefixed with /api.
Handles user registration, login, and logout. Authentication is managed via an httpOnly cookie named token.
Registers a new user and sends an OTP to their email for verification.
- Request Body:
{ "userName": "johndoe", "fullName": "John Doe", "email": "john.doe@example.com", "password": "password123" } - Success Response (201):
{ "success": true, "message": "Otp sent successfully" } - Error Response (409 - User Exists):
{ "success": false, "message": "User already exist! Please Login" }
Verifies the OTP sent to the user's email, creates the user account, and logs them in.
- Request Body:
{ "otp": "123456", "email": "john.doe@example.com" } - Success Response (201):
- Sets a
tokencookie.
{ "success": true, "message": "User registered successfully", "user": { "_id": "60d0fe4f5311236168a109ca", "fullName": "John Doe", "userName": "johndoe", "email": "john.doe@example.com", "posts": [], "followers": [], "following": [] } } - Sets a
- Error Response (400 - Invalid OTP):
{ "success": false, "message": "Invalid OTP !" }
Logs in an existing user.
- Request Body:
{ "userName": "johndoe", "password": "password123" } - Success Response (200):
- Sets a
tokencookie.
{ "success": true, "message": { "_id": "60d0fe4f5311236168a109ca", "fullName": "John Doe", "userName": "johndoe", "email": "john.doe@example.com" } } - Sets a
- Error Response (403 - Incorrect Credentials):
{ "success": false, "message": "Incorrect id or password !" }
Logs out the current user by clearing the token cookie.
- Success Response (200):
{ "success": true, "message": "signOut successfully" }
Endpoints for managing user profiles and interactions.
Retrieves the profile of the currently logged-in user.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Success Response (200):
{ "_id": "60d0fe4f5311236168a109ca", "fullName": "John Doe", "userName": "johndoe", "email": "john.doe@example.com", "profileImage": "https://res.cloudinary.com/...", "bio": "Software developer and coffee enthusiast.", "gender": "male", "followers": [], "following": [], "posts": [ { "_id": "60d0fe4f5311236168a109cb", "title": "My First Post" } ], "saved": [] }
Retrieves the public profile of a user by their username.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Success Response (200):
{ "_id": "60d0fe4f5311236168a109df", "fullName": "Jane Smith", "userName": "janesmith", "profileImage": "http://...", "bio": "A short bio about Jane." } - Error Response (400 - User Not Found):
{ "message": "user not found" }
Follows or unfollows another user.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Success Response (200):
- When following:
{ "following": true, "message": "follow successfully" }- When unfollowing:
{ "following": false, "message": "unfollow successfully" }
Updates the profile of the currently logged-in user.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Request:
multipart/form-dataname(String)userName(String)bio(String)gender(String: "male" or "female")profileImage(File, optional)
- Success Response (200):
{ "_id": "60d0fe4f5311236168a109ca", "name": "Johnathan Doe", "userName": "johnathandoe", "bio": "Updated bio here.", "gender": "male", "profileImage": "https://res.cloudinary.com/..." } - Error Response (400 - Username Taken):
{ "message": "username already exist" }
Endpoints for managing posts and post-related interactions.
Creates a new post.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Request:
multipart/form-datatitle(String)description(String)tags(String, comma-separated)pdf(File, required)thumbnail(File, required)
- Success Response (201):
{ "_id": "60d0fe4f5311236168a109cb", "title": "Understanding APIs", "description": "A deep dive into REST APIs.", "postImage": "https://res.cloudinary.com/...", "pdf": "https://res.cloudinary.com/...", "createdBy": { "_id": "60d0fe4f5311236168a109ca", "fullName": "John Doe", "userName": "johndoe", "profileImage": "http://..." }, "tags": ["api", "development"], "likes": [], "comments": [] } - Error Response (400 - Inappropriate Content):
{ "message": "Inappropriate or unsafe content detected.", "score": 0.95 }
Retrieves a paginated list of all posts, sorted by creation date.
- Query Parameters:
page(Number, optional, default: 1)limit(Number, optional, default: 20)
- Success Response (200):
{ "page": 1, "limit": 20, "total": 50, "totalPages": 3, "data": [ { "_id": "...", "title": "Post Title", "postImage": "https://...", "createdBy": { "fullName": "Author Name", "userName": "author_username", "profileImage": "https://..." } } ] }
Retrieves a single post by its ID.
- Success Response (200):
{ "_id": "60d0fe4f5311236168a109cb", "title": "Understanding APIs", "description": "A deep dive into REST APIs.", "postImage": "https://res.cloudinary.com/...", "pdf": "https://res.cloudinary.com/...", "createdBy": { "_id": "60d0fe4f5311236168a109ca", "fullName": "John Doe", "userName": "johndoe", "profileImage": "http://..." }, "tags": ["api", "development"], "likes": ["60d0fe4f5311236168a109df"], "comments": [ { "createdBy": { "_id": "60d0fe4f5311236168a109df", "fullName": "Jane Smith", "userName": "janesmith" }, "message": "This is a great post!", "timestamp": "2023-10-27T10:00:00.000Z" } ], "createdAt": "2023-10-27T09:00:00.000Z", "updatedAt": "2023-10-27T10:00:00.000Z" }
Retrieves all posts created by the logged-in user.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Success Response (200):
[ { "_id": "60d0fe4f5311236168a109cb", "title": "Understanding APIs", "description": "A deep dive into REST APIs.", "postImage": "https://res.cloudinary.com/...", "pdf": "https://res.cloudinary.com/...", "createdBy": { "_id": "60d0fe4f5311236168a109ca", "fullName": "John Doe", "userName": "johndoe", "profileImage": "http://..." }, "tags": ["api", "development"], "likes": [], "comments": [] } ]
Toggles a "like" on a post for the logged-in user.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Success Response (200): The updated post object with the new
likesarray.{ "_id": "60d0fe4f5311236168a109cb", "title": "Understanding APIs", "likes": [ "60d0fe4f5311236168a109ca" ], "comments": [], "createdBy": { "_id": "...", "fullName": "John Doe" } }
Toggles a "save" on a post for the logged-in user.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Success Response (200): The updated user object with the populated
savedarray.{ "_id": "60d0fe4f5311236168a109ca", "fullName": "John Doe", "userName": "johndoe", "saved": [ { "_id": "60d0fe4f5311236168a109cb", "title": "Understanding APIs", "description": "A deep dive into REST APIs." } ] }
Adds a comment to a specific post.
- Authentication: Requires a valid
tokensent as anhttpOnlycookie. - Request Body:
{ "message": "This is a great post!" } - Success Response (200): The updated post object with the new comment included.
{
"_id": "60d0fe4f5311236168a109cb",
"title": "Understanding APIs",
"comments": [
{
"createdBy": {
"_id": "...",
"fullName": "Jane Smith",
"userName": "janesmith",
"profileImage": "http://..."
},
"message": "This is a great post!",
"timestamp": "2023-10-27T10:00:00.000Z",
"_id": "..."
}
]
}While this API provides a solid foundation, here are some suggestions for enhancing it for a production environment:
-
API Rate Limiting: To prevent abuse and ensure service stability, implement rate limiting on endpoints. Libraries like
express-rate-limitcan be easily integrated. -
Advanced Input Validation: Add more robust validation and sanitization for all user inputs to enhance security and data integrity. Consider using libraries like
express-validatororJoi. -
Automated Testing: Introduce a testing framework (e.g., Jest, Mocha with Supertest) to write unit and integration tests. This will help ensure the reliability of your API as you add new features.
-
Centralized Error Handling: Create a dedicated middleware for handling all errors. This makes error responses more consistent and keeps your controller logic cleaner.
-
API Versioning: For future scalability, consider versioning your API (e.g.,
/api/v1/...). This allows you to introduce breaking changes without affecting existing client applications. -
Security Headers: Use a library like
helmetto set various HTTP headers that can help protect your application from common web vulnerabilities like Cross-Site Scripting (XSS) and clickjacking.