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
54 changes: 48 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,53 @@
# Project API
# Happy Thoughts API

This project includes the packages and babel setup for an express server, and is just meant to make things a little simpler to get up and running with.
A REST API for the Happy Thoughts app. Built with Express.js and MongoDB.

## Getting started
**Live:** https://js-project-api-7ve2.onrender.com

Install dependencies with `npm install`, then start the server by running `npm run dev`
**Frontend:** [js-project-happy-thoughts](https://github.com/Qabalany/js-project-happy-thoughts)

## View it live
## Tech

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.
Express, MongoDB Atlas, Mongoose, bcrypt, JWT, dotenv, Babel, Render

## Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/` | API docs |
| GET | `/thoughts` | Get all thoughts |
| GET | `/thoughts/:id` | Get one thought |
| POST | `/thoughts` | Create thought |
| POST | `/thoughts/:id/like` | Like a thought |
| PUT | `/thoughts/:id` | Update thought (auth required) |
| DELETE | `/thoughts/:id` | Delete thought (auth required) |
| POST | `/signup` | Register user |
| POST | `/login` | Login and get token |

### Query Params (GET /thoughts)

| Param | Default | Example |
|-------|---------|---------|
| `sort` | `date` | `?sort=hearts` |
| `order` | `desc` | `?order=asc` |
| `minHearts` | — | `?minHearts=5` |
| `page` | `1` | `?page=2` |
| `limit` | `20` | `?limit=10` |

## Run Locally

```bash
npm install
npm run dev
```

Create a `.env` file:
```
MONGODB_URI=your_mongodb_connection_string
JWT_SECRET=your_secret_key
PORT=8080
```

## View It Live

https://js-project-api-7ve2.onrender.com
26 changes: 26 additions & 0 deletions data/thoughts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"id": "1",
"message": "I love learning backend development!",
"hearts": 5,
"createdAt": "2026-02-01T10:30:00Z"
},
{
"id": "2",
"message": "Coffee and code make me happy",
"hearts": 12,
"createdAt": "2026-02-02T11:00:00Z"
},
{
"id": "3",
"message": "Finally understanding Express.js!",
"hearts": 8,
"createdAt": "2026-02-03T14:20:00Z"
},
{
"id": "4",
"message": "Building APIs is actually fun",
"hearts": 3,
"createdAt": "2026-02-04T09:15:00Z"
}
]
31 changes: 31 additions & 0 deletions models/Thought.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import mongoose from "mongoose"

// Define the Thought schema
const ThoughtSchema = new mongoose.Schema({
message: {
type: String,
required: [true, "Message is required"],
minlength: [5, "Message must be at least 5 characters"],
maxlength: [140, "Message cannot exceed 140 characters"],
trim: true
},
hearts: {
type: Number,
default: 0,
min: 0
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
default: null // null = anonymous thought
},
createdAt: {
type: Date,
default: Date.now
}
})

// Create the Thought model
const Thought = mongoose.model("Thought", ThoughtSchema)

export default Thought
56 changes: 56 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import mongoose from "mongoose"
import bcrypt from "bcrypt"

// Define the User schema
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: [true, "Username is required"],
unique: true,
minlength: [3, "Username must be at least 3 characters"],
maxlength: [30, "Username cannot exceed 30 characters"],
trim: true
},
email: {
type: String,
required: [true, "Email is required"],
unique: true,
trim: true,
lowercase: true,
match: [/^\S+@\S+\.\S+$/, "Please enter a valid email address"]
},
password: {
type: String,
required: [true, "Password is required"],
minlength: [6, "Password must be at least 6 characters"]
},
createdAt: {
type: Date,
default: Date.now
}
})

// Hash password before saving
UserSchema.pre("save", async function () {
// Only hash if password is new or modified
if (!this.isModified("password")) return

const salt = await bcrypt.genSalt(10)
this.password = await bcrypt.hash(this.password, salt)
})

// Method to compare passwords
UserSchema.methods.comparePassword = async function (candidatePassword) {
return bcrypt.compare(candidatePassword, this.password)
}

// Remove password from JSON output
UserSchema.methods.toJSON = function () {
const user = this.toObject()
delete user.password
return user
}

const User = mongoose.model("User", UserSchema)

export default User
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"bcrypt": "^6.0.0",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"express": "^4.17.3",
"express-list-endpoints": "^7.1.1",
"jsonwebtoken": "^9.0.3",
"mongoose": "^9.1.5",
"nodemon": "^3.0.1"
}
}
Loading