Conversation
qabalany
left a comment
There was a problem hiding this comment.
Hi Iris! I'm really impressed with your API implementation! You've built a secure, well-structured application with authentication that shows strong understanding of backend development principles. Here are my observations
| const existingUser = await User.findOne({ email: email.toLowerCase() }); | ||
|
|
||
| if (existingUser) { | ||
| return res.status(400).json({ success: false, message: "User already exists" }); | ||
| } | ||
|
|
||
| const salt = bcrypt.genSaltSync(); | ||
| const hashedPassword = bcrypt.hashSync(password, salt); | ||
| const user = new User({ email, password: hashedPassword }); | ||
| await user.save(); | ||
|
|
||
| res.status(201).json({ | ||
| success: true, | ||
| response: { email: user.email, id: user._id, accessToken: user.accessToken }, | ||
| }); | ||
| } catch (error) { | ||
| res.status(400).json({ success: false, response: error }); | ||
| } | ||
| }); | ||
|
|
||
| // LOGIN | ||
| router.post("/login", async (req, res) => { | ||
| try { | ||
| const { email, password } = req.body; | ||
| const user = await User.findOne({ email: email.toLowerCase() }); |
There was a problem hiding this comment.
I love that you're normalizing emails to lowercase in both signup and login! This prevents issues where a user signs up with "User@Email.com" but tries to log in with "user@email.com". This kind of thoughtful UX consideration makes the API much more user-friendly.
You could add one more layer of polish by also trimming whitespace:
const email = req.body.email.toLowerCase().trim();
| // AUTHENTICATION MIDDLEWARE | ||
| // This function sits between the request and the actual route logic | ||
| const authenticateUser = async (req, res, next) => { | ||
| const accessToken = req.header("Authorization"); | ||
| try { | ||
| const user = await User.findOne({ accessToken: accessToken }); | ||
| if (user) { | ||
| req.user = user; | ||
| next(); | ||
| } else { | ||
| res.status(401).json({ success: false, response: "Please log in" }); | ||
| } | ||
| } catch (error) { | ||
| res.status(500).json({ success: false, response: error.message }); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Your authenticateUser middleware is beautifully implemented!
he way you're passing req.user to the next handler (line 34) is particularly clever - it makes the authenticated user available throughout the request lifecycle. Excellent work!
| const UserSchema = new mongoose.Schema({ | ||
| email: { | ||
| type: String, | ||
| unique: true, | ||
| required: true, | ||
| }, | ||
| password: { | ||
| type: String, | ||
| required: true, | ||
| }, | ||
| accessToken: { | ||
| type: String, | ||
| // this generates a long random string as the user's secret token | ||
| default: () => crypto.randomBytes(128).toString("hex"), | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Well-Structured Schema Design!
The crypto token generation (line 17) is particularly impressive, you're creating a highly secure, random token.
| if (!mongoose.Types.ObjectId.isValid(id)) { | ||
| return res.status(400).json({ success: false, message: "Invalid ID" }); | ||
| } |
There was a problem hiding this comment.
I really appreciate that you're validating the MongoDB ObjectId before querying! This prevents unnecessary database calls and potential errors.
| response: { email: user.email, id: user._id, accessToken: user.accessToken }, | ||
| }); | ||
| } catch (error) { | ||
| res.status(400).json({ success: false, response: error }); |
There was a problem hiding this comment.
In the signup catch block, you're returning the full error object. While this is helpful for debugging, you might want to provide more user friendly messages in production:
res.status(400).json({
success: false,
message: "Unable to create account. Please check your details and try again."
});
| const Message = mongoose.model( | ||
| "Message", | ||
| new mongoose.Schema({ | ||
| message: { | ||
| type: String, | ||
| required: true, | ||
| minlength: 5, | ||
| maxlength: 140, | ||
| trim: true, | ||
| }, | ||
| hearts: { type: Number, default: 0 }, | ||
| createdAt: { type: Date, default: Date.now }, | ||
| }) | ||
| ); |
There was a problem hiding this comment.
Your Message model works great! For consistency and maintainability, you might consider moving it to a separate file like models/Message.js, similar to how you organized the User model. This would make the server.js file cleaner and follow the same pattern you've already established. It's a small organizational win that makes the codebase easier to navigate!
Please include your Render link here.
https://js-project-api-w19.onrender.com/