Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/dev0302/nextjs-project-1/llms.txt

Use this file to discover all available pages before exploring further.

AnonMessage lets any visitor send a message to a registered user without creating an account. Every user gets a unique public profile URL. As long as the recipient has message acceptance turned on, anyone who knows the link can send them a message — no authentication, no tracking.

Public Profile URL

Every registered user has a publicly accessible profile page at:
/u/[username]
No login is required to visit this page or submit a message. The page is a standard Next.js dynamic route that receives the username parameter from the URL, looks up the user, and renders the SendMessageProfile client component.

Sending a Message

The sender types their message in the text input on the profile page. When they click Send It, the SendMessageProfile component calls POST /api/send-message with a JSON body containing username and content:
// SendMessageProfile.tsx — send action
const response = await axios.post("/api/send-message", { username, content });
The API route processes the request in order:
  1. Finds the user document by username.
  2. Checks the isAcceptingMessages flag — returns 403 if it is false.
  3. Constructs a new message object with content and createdAt.
  4. Pushes it onto the user’s messages array and calls user.save().
// POST /api/send-message — core logic
const { username, content } = await req.json();

const user = await User.findOne({ username });

if (!user) {
  return NextResponse.json(
    { success: false, message: "User not found" },
    { status: 404 }
  );
}

if (!user.isAcceptingMessages) {
  return NextResponse.json(
    { success: false, message: "User is not accepting messages" },
    { status: 403 }
  );
}

const newMessage = {
  content,
  createdAt: new Date(),
};

user.messages.push(newMessage as Message);
await user.save();
A successful send returns 201 Created with { success: true, message: "Message sent successfully" }.

Message Content Validation

The content field is validated client-side and server-side against a Zod schema that enforces a minimum of 10 characters and a maximum of 300 characters:
// src/app/schemas/messageSchema.ts
import z from "zod";

export const messageSchema = z.object({
  content: z
    .string()
    .min(10, { message: "content must be of min length 10 chars" })
    .max(300, { message: "content must not be longer than 300 chars" }),
});

Message Storage Model

Messages are stored as embedded subdocuments inside the User document — there is no separate messages collection. Each message subdocument contains only two fields:
// src/app/models/User.ts — MessageSchema
const MessageSchema: mongoose.Schema<Message> = new mongoose.Schema({
  content: {
    type: String,
    required: true,
  },
  createdAt: {
    type: Date,
    required: true,
    default: Date.now,
  },
});

// Embedded inside UserSchema
const UserSchema: mongoose.Schema<IUser> = new mongoose.Schema({
  username: { type: String, required: true, unique: true, trim: true },
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
  isVerified: { type: Boolean, default: false },
  isAcceptingMessages: { type: Boolean, default: true },
  messages: [MessageSchema],
});
A stored user document looks like this:
{
  "_id": "664f1c2a...",
  "username": "alice",
  "email": "alice@example.com",
  "password": "$2b$10$...",
  "isVerified": true,
  "isAcceptingMessages": true,
  "messages": [
    { "_id": "664f1c3b...", "content": "Your work is really inspiring!", "createdAt": "2025-01-15T10:23:00.000Z" },
    { "_id": "664f1c4c...", "content": "What got you into this field?", "createdAt": "2025-01-14T08:11:00.000Z" }
  ]
}

Acceptance Control

Users can pause and resume message delivery at any time from their dashboard. The MessageToggle component on the dashboard calls POST /api/accept-messages whenever the user flips the switch:
// MessageToogle.tsx — toggle handler
const toggle = async () => {
  const newValue = !isAccepting;
  setIsAccepting(newValue);
  setIsUpdating(true);
  try {
    const res = await axios.post("/api/accept-messages", {
      acceptMessage: newValue,
    });
    toast.success(res.data.message);
  } catch (error) {
    setIsAccepting(!newValue); // revert on failure
    // ...
  }
};
The API route authenticates the request via getServerSession, then updates the isAcceptingMessages field on the user document:
// POST /api/accept-messages — database update
const updatedUser = await User.findByIdAndUpdate(
  userId,
  { isAcceptingMessages: acceptMessage },
  { new: true }
);
When a user turns off message acceptance, POST /api/send-message immediately begins returning 403 Forbidden with the message "User is not accepting messages". The sender sees this error on the profile page. No message is stored, and the user’s messages array is not modified.

No Sender Identity

The API stores only the content string and a server-generated createdAt timestamp. No sender IP address, device fingerprint, session cookie, or any other identifying information is recorded in the message subdocument or elsewhere in the request handler. Anonymity is structural — there is simply no code path that captures it.

Build docs developers (and LLMs) love