Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/emilydaykin/neighbour-needs-api
👥 The back end of a full stack MERN application with CRUD functionality that allows users to find people in their neighbourhood who can help with anything from electronics repair to therapy. Users can create posts and leave reviews and ratings.. GA Project 3
https://github.com/emilydaykin/neighbour-needs-api
axios bcrypt express heroku-deployment jwt-authentication mongo-sanitize mongodb mongodb-atlas mongoose nodejs
Last synced: 7 days ago
JSON representation
👥 The back end of a full stack MERN application with CRUD functionality that allows users to find people in their neighbourhood who can help with anything from electronics repair to therapy. Users can create posts and leave reviews and ratings.. GA Project 3
- Host: GitHub
- URL: https://github.com/emilydaykin/neighbour-needs-api
- Owner: emilydaykin
- Created: 2022-04-03T20:20:40.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2022-12-17T14:06:11.000Z (almost 2 years ago)
- Last Synced: 2024-09-09T17:20:24.520Z (2 months ago)
- Topics: axios, bcrypt, express, heroku-deployment, jwt-authentication, mongo-sanitize, mongodb, mongodb-atlas, mongoose, nodejs
- Language: JavaScript
- Homepage:
- Size: 23.7 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Neighbour Needs
Neighbour Needs is a full stack MERN application (MongoDB, Express, React and Node) that allows users to find people in their neighbourhood who can help with anything from maths tutoring and interior design, to plumbing and therapy. This project was created by Ana Borges, Emily Daykin and Mohamed Mohamed in the span of just over a week. For a full list of this app's features, see the [Features](#features) section below.**This repo contains code for the back end server only; code for the front end client lives [here](https://github.com/emilydaykin/Neighbour-Needs-Client).**
## Installation
- Check out the live application [here](https://neighbour-needs.netlify.app/)!
- Feel free to register and then use your own login credentials, or try a demo one using:
- Username: `[email protected]`
- Password: `Password1!@`
- Or run it locally (make sure you have a local version of MongoDB running):
- [Front End](https://github.com/momoh66/ga-project3-client): Clone this repo, → run `npm install` → run `npm run start:client`
- Back End: Clone this repo → run `npm install` → run `npm run seed` → run `npm run start:server`## Application Walkthrough
### Home & About Pages
### Sidebar, Welcome Banner & Login Page
### Feed & Profiles Page
### Creating a New Post
### Neighbourhoods & Services Pages
### Individual and Filtered Profile Pages
### Responsive Design
## Tech Stack
### Front End
- React Framework (Single Page Application)
- API Handling: Axios
- Pure CSS with Sass
- React-Router-Dom### Back End
- Server: Node.js & Express
- Database: MongoDB & Mongoose
- Safeguarding from injection attacks: Express Mongo Sanitize
- Password Encryption: Bcrypt
- Authentication: JSON Web Token (JWT)### Collaboration & Development
- Git, GitHub
- Trello for project management
- Postman for API testing
- Excalidraw for wireframing
- Npm
- Deployment:
- Front End: Netlify
- Back End: ~~Heroku~~ Render (& Mongo Atlas)## Features
- Display of all profiles, and routing to an individual profile page with more information and a comments area when clicked on
- Real time searching through all profiles by name, location, or service offered
- Minimalist top navbar with a more detailed slide-in-out sidebar
- Log In and Register functionality
- Once logged in:
- A user icon appears in the navbar, as well as a personalised welcome banner, which redirects to the user's profile page
- The user can create a post
- The user can leave a comment on any profile
- Only the same user who commented/posted can remove their comment and post, no one else's
- Filtering through service type or location via their respective pages## Architecture:
- Front End:
- React Components to compartmentalise code
- React Hooks for state management and handling side effects
- Scss stylesheets per react component
- Single Page Application (`react-router-dom`) using `Link`, `useNavigate`, `useLocation` and `useParams`
- Back End:
- All security checks (user access credentials) done in the back end:
- Email validation (correct format and uniqueness)
- Password validation (encryption and strength: minimum of 8 characters, at least one lowercase & uppercase letter and number)
- Obscuring the password response from the front end
- Login credentials expire after 6 hours
- Secure routing middelware to verify logged in users, same users (only that same user can delete their comment for example) and admin users
- Error handling middleware to assist with debugging
- 3 interlinked schema models in MongoDB for profiles, comments and posts
- Data seeding of 25 user profiles, 15 comments and 3 posts.## Featured Code Snippets
### Front End
#### New post pop-up (only when authenticated) using css `position: absolute`. User can also delete their posts only`.
```
const [createPostPopup, setCreatePostPopup] = useState(false);
const [newPostData, setNewPostData] = useState({
text: '',
service: '',
urgency: ''
});const createPostClicked = () => setCreatePostPopup(!createPostPopup);
function handlePostInputChange(e) {
setNewPostData({ ...newPostData, [e.target.name]: e.target.value });
}
async function handleSubmitPost(e) {
e.preventDefault();
await createPost(newPostData);
setNewPostData({ text: '', service: '', urgency: '' });
setCreatePostPopup(!createPostPopup);
getPostData();
}async function handleDeletePost(postId) {
await deletePost(postId);
getPostData();
}
```### Back End
#### Secure Route middleware to verify authentication and access rights
```
import jwt from 'jsonwebtoken';
import Profile from '../models/profile.js';
import { secret } from '../config/environment.js';const secureRoute = async (req, res, next) => {
try {
const authToken = req.headers.authorization;
if (!authToken || !authToken.startsWith('Bearer')) {
return res
.status(401)
.send({ message: 'Unauthorised. Auth Token incorrect or does not exist' });
} else {
const token = authToken.replace('Bearer ', '');
jwt.verify(token, secret, async (err, data) => {
if (err) {
return res.status(400).json({ message: "Unauthorised. JWT can't verify." });
} else {
const user = await Profile.findById(data.profileId);
if (!user) {
return res.status(401).json({ message: 'Unauthorised. User not in database' });
} else {
req.currentUser = user;
next();
}
}
});
}
} catch (err) {
return res.status(401).send({ message: 'Unauthorised' });
}
};export default secureRoute;
```
#### Interlinked profile and comments model schema
```
export const commentSchema = new mongoose.Schema(
{
text: { type: String, required: true, maxLength: 300 },
rating: { type: Number, required: true, min: 1, max: 5 },
createdById: {
type: mongoose.Schema.ObjectId,
ref: 'Profile',
required: true
},
createdByName: {
type: String
},
createdBySurname: {
type: String
}
},
{ timestamps: true }
);const profileSchema = new mongoose.Schema({
firstName: { type: String, required: [true, 'First name required'] },
surname: { type: String, required: [true, 'Surname required'] },
email: {
type: String,
required: [true, 'Email required'],
unique: true,
validate: (email) => emailRegex.test(email)
},
password: {
type: String,
required: [true, 'Password required'],
minlength: [8, 'Password must be a minimum of 8 characters'],
validate: (password) => passwordRegex.test(password)
},
isHelper: { type: Boolean },
averageRating: { type: String },
services: { type: Array },
bio: { type: String },
city: { type: String, required: [true, 'City required'] },
region: { type: String, required: [true, 'Region required'] },
imageProfile: { type: String },
imageService: { type: String },
comments: [commentSchema],
posts: { type: Array },
isAdmin: { type: Boolean }
});
```## Future Improvements & Bugs
If we'd had more time as a group, we would've loved to implement an edit profile function (where a user can edit their own profile, become a helper, add a bio etc), as well as messaging functionality where users can reach out to helpers to arrange appointments and request more information. One unsolved problem we had was registered a new user a helper: when a new user registers and fills out the services they can help out with, they don't get saved to the database as a helper.