Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 31 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,39 @@
# Project Happy Thoughts API

Replace this readme with your own information about your project.

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.
This project involves creating an API for a "Happy Thoughts" platform, a positivity-focused version of Twitter. The assignment is to build a backend API that serves as a replacement for the Technigo-provided API, allowing a React frontend to interact with it seamlessly. The API will handle features like posting new thoughts, listing recent thoughts, and updating the number of likes (hearts) for each thought.

## The problem

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
To complete this project, I needed to design and implement a fully functional RESTful API using Node.js, Express, and MongoDB. The primary challenge was to ensure the API followed the specific requirements, such as input validation, endpoint functionality, and seamless integration with the existing frontend.

### Approach and Tools:

- **Planning:** I broke down the project into key functionalities, starting with creating the Thought model, then implementing and testing each endpoint.
- **Technologies:**
- Backend Framework: Express.js to manage API routes and middleware.
- Database: MongoDB with Mongoose for data modeling and validation.
- Deployment: Deployed the API on Render to make it accessible for frontend integration.
- Validation: Used Mongoose schema validation and manual checks to ensure user input met all requirements.

### Key Steps:

- **Thought Model:**
Defined a Mongoose schema with properties:
- message (required, 5-140 characters).
- hearts (default to 0, unassignable during creation).
- createdAt (default to current timestamp, unassignable during creation).
Endpoints:
- GET /thoughts: Returned up to 20 recent thoughts, sorted by creation date.
- POST /thoughts: Allowed new thoughts to be submitted, with proper validation and error handling.
- POST /thoughts/:thoughtId/like: Incremented the hearts property for a specific thought.
Validation and Error Handling:
- Ensured invalid input resulted in clear error messages and a 400 Bad Request status.
- Checked for non-existent thought IDs in the POST /thoughts/:thoughtId/like endpoint.
Frontend Integration:
- Updated the React app to use the new API by replacing the Technigo URL with the deployed API URL.

## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
Frontend Netlify: https://happy-thoughts-project-technigo.netlify.app/
Frontend GitHub: https://github.com/EmelieNyberg/project-happy-thoughts-vite-ek
Backend Render: https://project-happy-thoughts-api-ek.onrender.com/
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^4.17.3",
"express-list-endpoints": "^7.1.1",
"mongoose": "^8.0.0",
"nodemon": "^3.0.1"
}
}
}
99 changes: 96 additions & 3 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,117 @@
import cors from "cors";
import express from "express";
import mongoose from "mongoose";
import dotenv from "dotenv";
import expressListEndpoints from "express-list-endpoints";

const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo";
dotenv.config();

const mongoUrl = process.env.MONGO_URL; //|| "mongodb://localhost/project-mongo";
mongoose.connect(mongoUrl);
mongoose.Promise = Promise;

// Defines the port the app will run on. Defaults to 8080, but can be overridden
// when starting the server. Example command to overwrite PORT env variable value:
// PORT=9000 npm start
const port = process.env.PORT || 8080;
const port = process.env.PORT || 3000;
const app = express();

// Add middlewares to enable cors and json body parsing
app.use(cors());
app.use(express.json());

const { Schema, model } = mongoose;

// Define mongoose Schema since more complex than just using mongoose model
const thoughtSchema = new Schema({
message: {
type: String,
required: true,
minlength: 5,
maxlength: 140
},
hearts: {
type: Number,
default: 0
},
createdAt: {
type: Date,
default: Date.now
}
});

const Thought = model('Thought', thoughtSchema);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Be consistent with double quotes


// Start defining your routes here
app.get("/", (req, res) => {
res.send("Hello Technigo!");
res.json(expressListEndpoints(app));
});

app.get("/thoughts", async (req, res) => {
const thoughts = await Thought.find().sort({ createdAt: 'desc' }).limit(20).exec();
res.json(thoughts);
});

app.post("/thoughts", async (req, res) => {
//Retrieve info that is sent by the user to our API endpoint
// I use {} around message to make sure ONLY message can be sent in by the user, not hearts and createdAt.
const { message } = req.body;
// Use our mongoose model to create the database entry
const thought = new Thought({ message });

try {
// Success
const savedThought = await thought.save();
res.status(201).json(savedThought);
} catch (err) {
res.status(400).json({ message: "Could not save thought to database", error: err.errors });
}
});

// app.post("/thoughts", async (req, res) => {
// //Retrieve info that is sent by the user to our API endpoint
// // I use {} around message to make sure ONLY message can be sent in by the user, not hearts and createdAt.
// const { message } = req.body;

// try {
// // Use our mongoose model to create the database entry
// const thought = await new Thought({ message }).save();

// res.status(201).json({
// success: true,
// response: thought,
// message: "thought is created"
// });
// } catch (err) {
// res.status(400).json({
// success: false,
// response: err.errors,
// message: "Could not save thought to database"
// });
// }
// });
Comment on lines +71 to +92

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remove unused code


app.post("/thoughts/:thoughtId/like", async (req, res) => {
// Get thoughtId from URL-param
// Could also do like this: const thoughtId = req.params.thoughtId;
const { thoughtId } = req.params;

try {
// Find a thought with right ID and increase hearts by 1
const updatedThought = await Thought.findByIdAndUpdate(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thoughtId, // Find thought based on ID
{ $inc: { hearts: 1 } }, // Increments hearts with 1
{ new: true } // Return the updated thought
);

if (updatedThought) {
res.status(200).json(updatedThought); // Send back the updated thought
} else {
res.status(404).json({ message: "Thought not found" }); // If no thought found
}
} catch (err) {
res.status(400).json({ message: "Could not update hearts", error: err.message });
}
});

// Start the server
Expand Down