Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
32ed946
Adds a filter for ID
julialindstrand Jan 21, 2026
a926445
Adds filter of thoughts with 0 likes
julialindstrand Jan 21, 2026
48b2bcb
Troubleshooting
julialindstrand Feb 9, 2026
1613e9b
More troubleshooting
julialindstrand Feb 9, 2026
55a9c44
Adds users
julialindstrand Feb 10, 2026
04d5efb
Error when deploying
julialindstrand Feb 10, 2026
dcd0fcc
Comments out errors
julialindstrand Feb 10, 2026
ccc290d
Adds bcrypt
julialindstrand Feb 10, 2026
dbe9041
Installes mongoose save
julialindstrand Feb 10, 2026
3f0b6c0
Add mongoose dependency
julialindstrand Feb 10, 2026
ac8f163
Troubleshooting again
julialindstrand Feb 10, 2026
66444aa
More troubleshooting
julialindstrand Feb 10, 2026
d4b1da0
Tried fixing with the AI of render
julialindstrand Feb 10, 2026
03bcb12
Adds mongoose in user route
julialindstrand Feb 10, 2026
845efa4
Adds .env
julialindstrand Feb 10, 2026
895ad54
Adds dotenv
julialindstrand Feb 10, 2026
9a9be37
Error message: cant find open port
julialindstrand Feb 10, 2026
d8c2a88
Removes consolelog
julialindstrand Feb 10, 2026
fe61e99
Adds .js
julialindstrand Feb 10, 2026
1113758
Adds type field in json
julialindstrand Feb 10, 2026
d474dc5
Tries to move a curlybracket
julialindstrand Feb 10, 2026
3ab1cb9
Removed an experimental const
julialindstrand Feb 10, 2026
55bda57
Removes reset
julialindstrand Feb 11, 2026
ee39005
Closes the if-statment earlier
julialindstrand Feb 11, 2026
84a90d6
Removes one /thougths
julialindstrand Feb 11, 2026
b415baf
Adds accesstoken
julialindstrand Feb 11, 2026
9b1389d
Adds .js
julialindstrand Feb 11, 2026
dc0ff80
Changes the like endpoint
julialindstrand Feb 11, 2026
d8bb25a
Adds likes
julialindstrand Feb 11, 2026
dff0038
Installed cors
julialindstrand Feb 11, 2026
fd8e495
Adds corsfix
julialindstrand Feb 11, 2026
688a4cb
Removes cors
julialindstrand Feb 11, 2026
2d24f5d
Adds allow all corse
julialindstrand Feb 11, 2026
1c58c75
Fixed a few of the final problems
julialindstrand Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "project-api",
"version": "1.0.0",
"description": "Project API",
"type": "module",
"scripts": {
"start": "babel-node server.js",
"dev": "nodemon server.js --exec babel-node"
Expand All @@ -12,8 +13,12 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"cors": "^2.8.5",
"bcrypt": "^6.0.0",
"cors": "^2.8.6",
"dotenv": "^17.2.4",
"express": "^4.17.3",
"express-list-endpoints": "^7.1.1",
"mongoose": "^9.2.0",
"nodemon": "^3.0.1"
}
}
248 changes: 248 additions & 0 deletions routes/thoughtRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import express from "express"
import mongoose from "mongoose"
import { User } from "./userRoutes.js"

const router = express.Router();

const authenticateUser = async (req, res, next) => {
try {
const user = await User.findOne({
accessToken: req.header('Authorization').replace("Bearer ", ""),
})
if (user) {
req.user = user
next()
} else {
res.status(401).json({
message: "Authentication missing / invalid",
loggedOut: true
})
}
} catch (error) {
res.status(500).json({ message: "Internal server error", error: error.message })
}
}

// Thought schema

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Auth and schema I recommend to create separate files, so that the code will shorter and easy to follow.

const thoughtSchema = new mongoose.Schema({
message: { type: String, required: true },
hearts: { type: Number, default: 0 },
createdAt: { type: Date, default: Date.now },
userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true }
})

const Thought = mongoose.model('Thought', thoughtSchema)


// Show all thoughts
router.get("/thoughts", async (req, res) => {
try {
const thoughts = await Thought.find().sort({ createdAt: "desc" })
res.json(thoughts)

} catch (error) {
res.status(500).json({ error: "Failed to fetch thougts" })
}
})


// Show thoughts with likes
router.get("/thoughts/like", async (req, res) => {
const { hearts } = req.query

const dbQuery = {}
if (hearts !== undefined) {
const heartsNum = Number(hearts)
if (!Number.isNaN(heartsNum)) {
dbQuery.hearts = heartsNum
}
}

try {
const thoughts = await Thought.find(dbQuery)
if (thoughts.length === 0) {
return res.status(404).json({
success: false,
response: [],
message: "No thoughts match the query"
})
}

return res.status(200).json({
success: true,
response: thoughts,
message: "Thoughts retrieved"
})
} catch (error) {
console.error(error);
return res.status(500).json({
success: false,
response: [],
message: error
})
}
})


// Filter by ID
router.get("/thoughts/:id", async (req, res) => {
const id = req.params.id

try {
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(400).json({
success: false,
response: null,
message: "Invalid ID format",

})
}
const thought = await Thought.findById(id)

if (!thought) {
return res.status(404).json({
success: false,
response: null,
message: "Thought not found",
})
}

return res.status(200).json({
success: true,
response: thought,
message: "Success",
})
}

catch (error) {
return res.status(500).json({
success: false,
response: null,
message: error,
})
}
}
)


// Post
router.post("/thoughts", authenticateUser, async (req, res) => {
const { message } = req.body

try {
const newThought = await new Thought({
message, userId: req.user._id,
}).save()

if (!newThought) {
return res
.status(400)
.json({ success: false, data: null, message: "Failed to post thought" })
}

res.status(201).json({
success: true,
data: newThought,
message: "Thought created successfully."
})

} catch (error) {
console.log(error)
res.status(500).json({
success: false,
data: null,
message: error.message || "Server error"
})
}
})


// Edit
router.patch('/thoughts/:id', authenticateUser, async (req, res) => {
const { id } = req.params

try {
const thought = await Thought.findById(id);
if (!thought) {
return res.status(404).json({ error: "Thought not found" });
}
if (thought.userId.toString() !== req.user._id.toString()) {
return res.status(403).json({ error: "You can only edit your own thoughts" });
}

thought.message = req.body.message ?? thought.message;
thought.hearts = req.body.hearts ?? thought.hearts;
await thought.save();

return res.json({ success: true, thought });

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return HTTP status as well.
res.status(200).json(...)

} catch (err) {
return res.status(400).json({ error: "Invalid request", details: err.message });
}
})


// Like
router.post("/thoughts/:id/like", authenticateUser, async (req, res) => {
const { id } = req.params

if (!mongoose.Types.ObjectId.isValid(id)) {
return res
.status(400)
.json({ success: false, message: "Invalid thought ID" })
}

try {
const thought = await Thought.findById(id)
if (!thought) {
return res
.status(404)
.json({ success: false, message: "Thought not found" })
}

thought.hearts += 1
await thought.save()

return res
.status(200)
.json({ success: true, hearts: thought.hearts, message: "Liked!" })
} catch (err) {
console.error(err)
return res
.status(500)
.json({ success: false, message: err.message })
}
})


// Delete
router.delete("/thoughts/:id", authenticateUser, async (req, res) => {
const id = req.params.id;
try {
const thought = await Thought.findById(id)

if (!thought) {
return res.status(404).json({
success: false,
response: [],
message: "Thought not found"
})
}

await Thought.findByIdAndDelete(id)

res.status(200).json({
success: true,
response: id,
message: "Thought deleted successfully"
})

} catch (error) {
res.status(500).json({
success: false,
response: null,
message: error,
})
}
})

export default router
93 changes: 93 additions & 0 deletions routes/userRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import express from "express"
import bcrypt from "bcrypt"
import crypto from "crypto"
import mongoose from "mongoose"

const router = express.Router()

// User schema
const UserSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString("hex"),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to use crypto, you need to import crypto from crypto:)

},
})

export const User = mongoose.model('User', UserSchema)


// New User
router.post('/users/signup', async (req, res) => {
try {
const { email, password } = req.body

const existingUser = await User.findOne({
email: email.toLowerCase()
})

if (existingUser) {
return res.status(400).json({
success: false,
message: "User with this email already exists"
})
}

const salt = bcrypt.genSaltSync()
const hashedPassword = bcrypt.hashSync(password, salt)
const user = new User({ email, password: hashedPassword })

await user.save()

res.status(200).json({
success: true,
message: "User created successfully",
response: {
email: user.email,
id: user._id,
accessToken: user.accessToken,
},
})
} catch (error) {
res.status(400).json({
success: false,
message: 'Could not create user',
response: error,
})
}
})

// Log In
router.post('/users/login', async (req, res) => {
try {
const { email, password } = req.body
const user = await User.findOne({ email: email.toLowerCase() })

if (user && bcrypt.compareSync(password, user.password)) {
res.json({
success: true,
message: "Logged in successfully",
response: {
email: user.email,
id: user._id,
accessToken: user.accessToken
},
})
} else {
res.status(401).json({
success: false,
message: "Wrong e-mail or password",
response: null,
})
}
} catch (error) {
res.status(500).json({
success: false,
message: "Something went wrong",
response: error
})
}
})

export default router
Loading