Skip to content

Commit 8c92e98

Browse files
authored
Merge pull request #65 from SOURAV-ROY/ROY
docs: Add comprehensive API reference documentation and update express
2 parents 9ef2934 + e719085 commit 8c92e98

3 files changed

Lines changed: 247 additions & 11 deletions

File tree

docs/API_REFERENCE.md

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,237 @@
1+
# [NodeJs API](https://bootcamps.vercel.app)
2+
********************************************
3+
4+
### [Bootcamps](https://bootcamps.vercel.app/api/v1/bootcamps)
5+
- List all bootcamps in the database
6+
* Pagination
7+
* Select specific fields in result
8+
* Limit number of results
9+
* Filter by fields
10+
- Search bootcamps by radius from zipcode
11+
* Use a geocoder to get exact location and coords from a single address field
12+
- Get single bootcamp
13+
- Create new bootcamp
14+
* Authenticated users only
15+
* Must have the role "publisher" or "admin"
16+
* Only one bootcamp per publisher (admins can create more)
17+
* Field validation via Mongoose
18+
- Upload a photo for bootcamp
19+
* Owner only
20+
* Photo will be uploaded to local filesystem
21+
- Update bootcamps
22+
* Owner only
23+
* Validation on update
24+
- Delete Bootcamp
25+
* Owner only
26+
- Calculate the average cost of all courses for a bootcamp
27+
- Calculate the average rating from the reviews for a bootcamp
28+
29+
### [Courses](https://bootcamps.vercel.app/api/v1/courses)
30+
- List all courses for bootcamp
31+
- List all courses in general
32+
* Pagination, filtering, etc
33+
- Get single course
34+
- Create new course
35+
* Authenticated users only
36+
* Must have the role "publisher" or "admin"
37+
* Only the owner or an admin can create a course for a bootcamp
38+
* Publishers can create multiple courses
39+
- Update course
40+
* Owner only
41+
- Delete course
42+
* Owner only
43+
44+
### [Reviews](https://bootcamps.vercel.app/api/v1/reviews)
45+
- List all reviews for a bootcamp
46+
- List all reviews in general
47+
* Pagination, filtering, etc
48+
- Get a single review
49+
- Create a review
50+
* Authenticated users only
51+
* Must have the role "user" or "admin" (no publishers)
52+
- Update review
53+
* Must have the role "user" or "admin" (no publishers)
54+
- Delete review
55+
* Must have the role "user" or "admin" (no publishers)
56+
57+
### Users & Authentication
58+
- Authentication will be ton using JWT/cookies
59+
* JWT and cookie should expire in 30 days
60+
- User registration
61+
* Register as a "user" or "publisher"
62+
* Once registered, a token will be sent along with a cookie (token = xxx)
63+
* Passwords must be hashed
64+
- User login
65+
* User can login with email and password
66+
* Plain text password will compare with stored hashed password
67+
* Once logged in, a token will be sent along with a cookie (token = xxx)
68+
- User logout
69+
* Cookie will be sent to set token = none
70+
- Get user
71+
* Route to get the currently logged in user (via token)
72+
- Password reset (lost password)
73+
* User can request to reset password
74+
* A hashed token will be emailed to the users registered email address
75+
* A put request can be made to the generated url to reset password
76+
* The token will expire after 10 minutes
77+
- Update user info
78+
* Authenticated user only
79+
* Separate route to update password
80+
- User CRUD
81+
* Admin only
82+
- Users can only be made admin by updating the database field manually
83+
84+
## Security
85+
- Encrypt passwords and reset tokens
86+
- Prevent NoSQL injections
87+
- Add headers for security (helmet)
88+
- Prevent cross site scripting - XSS
89+
- Add a rate limit for requests of 100 requests per 10 minutes
90+
- Protect against http param polution
91+
- Use cors to make API public (for now)
92+
93+
## Documentation
94+
- Use Postman to create documentation
95+
- Use [docgen](https://github.com/thedevsaddam/docgen) to create HTML files from Postman JSON File
96+
- Add html files as the / route for the api
97+
98+
99+
100+
101+
## Reverse Populate
102+
### In Model (Options)
103+
```js
104+
toJSON: {virtuals: true},
105+
toObject: {virtuals: true}
106+
```
107+
```js
108+
BootcampSchema.virtual('courses', {
109+
ref: 'Course',
110+
localField: '_id',
111+
foreignField: 'bootcamp',
112+
justOne: false
113+
});
114+
```
115+
### In Controller
116+
```js
117+
query = Bootcamp.find(JSON.parse(queryString)).populate('courses');
118+
```
119+
## Course Being Removed From Bootcamp
120+
```js
121+
BootcampSchema.pre('remove', async function (next) {
122+
console.log(`Course being removed from bootcamp: ${this._id}`);
123+
await this.model('Course').deleteMany({bootcamp: this._id});
124+
next();
125+
})
126+
```
127+
```js
128+
const bootcamp = await Bootcamp.findById(req.params.id);
129+
bootcamp.remove();
130+
```
131+
## Calculating The Average CourseCost
132+
```js
133+
CourseSchema.statics.getAverageCost = async function (bootcampId) {
134+
const obj = await this.aggregate([
135+
{
136+
$match: {bootcamp: bootcampId}
137+
},
138+
{
139+
$group: {
140+
_id: '$bootcamp',
141+
averageCost: {$avg: '$tuition'}
142+
}
143+
}
144+
]);
145+
try {
146+
await this.model('Bootcamp').findByIdAndUpdate(bootcampId, {
147+
averageCost: Math.ceil(obj[0].averageCost / 10) * 10
148+
})
149+
} catch (errors) {
150+
console.log(errors);
151+
}
152+
}
153+
```
154+
```js
155+
//Call AverageCost After Add Course **********************
156+
CourseSchema.post('save', function () {
157+
this.constructor.getAverageCost(this.bootcamp);
158+
});
159+
160+
//Call AverageCost Before Remove Course ******************
161+
CourseSchema.pre('remove', function () {
162+
this.constructor.getAverageCost(this.bootcamp);
163+
});
164+
```
165+
## Encrypt Password Using bcryptjs
166+
```js
167+
UserSchema.pre('save', async function (next) {
168+
if (!this.isModified('password')) {
169+
next();
170+
}
171+
const salt = await bcrypt.genSalt(10);
172+
this.password = await bcrypt.hash(this.password, salt);
173+
});
174+
```
175+
## get Signed JWT
176+
```js
177+
UserSchema.methods.getSignedJwtToken = function () {
178+
return jwt.sign({id: this._id}, process.env.JWT_SECRET, {
179+
expiresIn: process.env.JWT_EXPIRE
180+
});
181+
};
182+
```
183+
## Match User Entered Password to Hashed Password
184+
```js
185+
UserSchema.methods.matchPassword = async function (enteredPassword) {
186+
return await bcrypt.compare(enteredPassword, this.password);
187+
};
188+
```
189+
## Grand Access to Specific Roles
190+
```js
191+
exports.authorize = (...roles) => {
192+
return (req, res, next) => {
193+
if (!roles.includes(req.user.role)) {
194+
return next(new ErrorResponse(`User Role ${req.user.role} is Not Authorize to access this route`, 403));
195+
}
196+
next();
197+
};
198+
};
199+
```
200+
## Bootcamp User Relationship
201+
```js
202+
req.body.user = req.user.id;
203+
204+
const publishedBootcamp = await Bootcamp.findOne({user: req.user.id});
205+
206+
if (publishedBootcamp && req.user.role !== 'admin') {
207+
return next(new ErrorResponse(`The User with ${req.user.id} Already Published a Bootcamp`, 400));
208+
}
209+
```
210+
## Make Sure User Is Bootcamp Owner
211+
```js
212+
if (bootcamp.user.toString() !== req.user.id && req.user.role !== 'admin') {
213+
return next(new ErrorResponse(`User ${req.user.id} Is Not Authorized to The Bootcamp`, 401));
214+
}
215+
```
216+
## Generate And Hash Password Token
217+
```js
218+
UserSchema.methods.getResetPasswordToken = function () {
219+
const resetToken = crypto.randomBytes(20).toString('hex');
220+
this.resetPasswordToken = crypto
221+
.createHash('sha256')
222+
.update(resetToken)
223+
.digest('hex');
224+
225+
this.resetPasswordExpire = Date.now() + 10 * 60 * 1000;
226+
return resetToken;
227+
};
228+
```
229+
## Prevent User From Submitting More Than 1 Review Per Bootcamp
230+
```js
231+
ReviewSchema.index({bootcamp: 1, user: 1}, {unique: true});
232+
```
233+
***
234+
1235
# API Reference
2236

3237
Backend API for the DevCamper application to the manage bootcams

package-lock.json

Lines changed: 11 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"cookie-parser": "^1.4.7",
3535
"cors": "^2.8.5",
3636
"dotenv": "^16.4.5",
37-
"express": "^5.1.0",
37+
"express": "^5.2.1",
3838
"express-fileupload": "^1.5.1",
3939
"express-mongo-sanitize": "^2.2.0",
4040
"express-rate-limit": "^7.4.1",
@@ -52,4 +52,4 @@
5252
"swagger-ui-express": "^5.0.1",
5353
"xss-clean": "^0.1.4"
5454
}
55-
}
55+
}

0 commit comments

Comments
 (0)