https://github.com/knacksteem/knacksteem-api/
https://github.com/knacksteem/knacksteem-api/pull/14
https://github.com/knacksteem/knacksteem-api/pull/15
"Do you have any talent? If yes! then KnackSteem is for you."
"Rewards people with talents, it can be any talent, anything you know how to do best is highly welcome on the platform. "
Source: Discord Channel :D
We use tags to improve our posts search functionality. This point will allow the post owner to edit the tags of this post if a mistake was made. This endpoint will require prior authentification before using it.
Related code:
/**
* Method to update the tags of a post
* @param {Object} req: url params
* @param {Function} res: Express.js response callback
* @param {Function} next: Express.js middleware callback
* @author Jayser Mendez
* @public
*/
exports.updateTags = async (req, res, next) => {
try {
const { permlink, tags } = req.body;
const { username } = res.locals;
const post = await Post.findOne({ permlink });
if (post.author !== username) {
return next({
status: httpStatus.UNAUTHORIZED,
message: 'You cannot edit someone else post',
});
}
await Post.update({ permlink }, { tags });
return res.status(httpStatus.OK).send({
status: httpStatus.OK,
message: 'Post edited correctly',
});
} catch (err) {
console.log(err);
return next({
status: httpStatus.INTERNAL_SERVER_ERROR,
message: 'Opps! Something is wrong in our server. Please report it to the administrator.',
error: err,
});
}
};
By default, stats endpoints were not showing if the current user has voted the current loaded post. Indeed, there was an inconsistency in the returned fields in comparison with other endpoints. Also, this was leading to errors in client-side since it is re-using components for the posts. To solve this issue, I replaced the username field (used to get posts by the user) to author and used the username field to confirm is the current user has voted this post. The isVoted field was added to the response to make it identically to the rest.
Related code (coded was truncated to improve visibility):
let isVoted = false;
// Check if there is a user provided in the params.
// If so, determine if this user has voted the post.
if (username) {
isVoted = helper.isVoted(response.active_votes, username);
}
Even I was not facing this issue, the frontend developer told me that CORS were denying the PUT method at his end. To solve this issue, we decided to allow all methods in development mode (and we've confirmed that it works properly in production mode).
Related code:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
next();
});
Categories were loaded directly from the client side. As an enhancement, I decided to load them dynamically from the server side since we may need to add or remove some categories in the future. to avoid a re-deploy of the client-side, those should be loaded from the backend.
Related code:
const mongoose = require('mongoose');
/**
* Category Schema
* @author Jayser Mendez
* @private
*/
const categorySchema = new mongoose.Schema({
key: {
type: String,
required: true,
trim: true,
lowercase: true,
unique: true,
},
name: {
type: String,
required: true,
trim: true,
},
});
// Declare index in category schema for faster query.
categorySchema.index({
key: 1,
name: 1,
}, { name: 'category_index' });
/**
* @typedef Category
*/
module.exports = mongoose.model('Category', categorySchema);
As explained above, categories are now dynamically loaded. Hence, an endpoint is needed to consume them. The endpoint will return a response in the exact way it was in the client side so not too much changes are needed.
Related code:
/**
* Get all the categories in database
* @param {Object} req: url params
* @param {Function} res: Express.js response callback
* @param {Function} next: Express.js middleware callback
* @public
* @author Jayser Mendez
*/
exports.getCategories = async (req, res, next) => {
try {
const categoriesList = await Category.find({}, { __v: 0, _id: 0 });
return res.status(httpStatus.OK).send({
status: httpStatus.OK,
results: categoriesList,
count: categoriesList.length,
});
} catch (err) {
return next({
status: httpStatus.INTERNAL_SERVER_ERROR,
message: 'Opps! Something is wrong in our server. Please report it to the administrator.',
error: err,
});
}
};
Since now categories are dynamic, a supervisor may be able to create categories. So, this endpoint was created with this functionality in mind.
Related code:
/**
* Create a new category in database
* @param {Object} req: url params
* @param {Function} res: Express.js response callback
* @param {Function} next: Express.js middleware callback
* @public
* @author Jayser Mendez
*/
exports.createCategory = async (req, res, next) => {
try {
const { name } = req.body;
// Strip all the empty spaces and special characters and convert to lower case
const key = name.replace(/[^A-Z0-9]+/ig, '').toLowerCase();
const newCategory = await new Category({ key, name });
await Category.create(newCategory);
return res.status(httpStatus.CREATED).send({
status: httpStatus.CREATED,
message: 'Category was correctly created',
});
} catch (err) {
// Return exact error if there is a collision in the key
if (err.message.includes('E11000')) {
return next({
status: httpStatus.NOT_ACCEPTABLE,
message: 'Opps! A category with this name already exists.',
error: err,
});
}
return next({
status: httpStatus.INTERNAL_SERVER_ERROR,
message: 'Opps! Something is wrong in our server. Please report it to the administrator.',
error: err,
});
}
};
A set of default categories should be added when the server run for the first time. To do so, I created a seed method to insert a batch of data in the database if the categories collection is empty.
Related code:
const Category = require('../models/category.model');
// Batch of initial categories
const initialCategories = [
{ key: 'vlog', name: 'VLog' },
{ key: 'graphics', name: 'Graphics' },
{ key: 'art', name: 'Art' },
{ key: 'knack', name: 'Knack' },
{ key: 'onealtruism', name: 'One Altruism' },
{ key: 'music', name: 'Music' },
{ key: 'humor', name: 'Joke/Humor' },
{ key: 'inspiring', name: 'Inspiring' },
{ key: 'visibility', name: 'Visibility' },
{ key: 'news', name: 'News' },
{ key: 'quotes', name: 'Quotes' },
{ key: 'techtrends', name: 'Tech Trends' },
{ key: 'blogposts', name: 'Blog Posts' },
];
/**
* Method to insert the initial batch of categories in the backend
* @public
* @author Jayser Mendez
*/
exports.seedCategories = async () => {
try {
const count = await Category.count();
// If this is the first time running, insert the categories
if (count === 0) {
initialCategories.map(async (category) => {
const newCategory = await new Category({
key: category.key,
name: category.name,
});
await Category.create(newCategory);
});
return true;
}
return false;
} catch (err) {
return false;
}
};
Feel free to contact @knowledges :)