Nodejs - How to route by name in url with express and mongoose? - javascript

I have an Express 4 server with CRUD routes for authors:
router.get('/authors', AuthorsController.index);
router.post('/authors', AuthorsController.store);
router.get('/authors/:name', AuthorsController.show);
router.put('/authors/:name', AuthorsController.update);
router.delete('/authors/:name', AuthorsController.remove);
Most tutorials I find will have the routes be /authors/:id and then do Author.findById(req.params.id). I'd instead like it to be the authors name so there are human readable URLs, like: /authors/jk-rowling.
Do I need to store the hyphenated string on the Authors model, what's the best way to achieve this with express and Mongoosejs?
My authors controller currently looks like this:
const AuthorsController = {
async index(req, res){
const authors = await Author.find().populate('books');
res.send(authors);
}
};
(I'm using express-async-errors package for the async-await functionality with express)
What's the best way to establish routes with human readable URLs for a CRUD REST API with Express and Mongoose?

You can index name field and in your views, find by name (assuming name is attribute of Author)
This requires no change in terms of db model. You can search via both id or name if needed.
For example
router.get('/authors/:name', AuthorsController.show);
will have below view
const AuthorsController = {
async show(req, res){
const authors = await Author.find({'name':req.params.name}).populate('books');
res.send(authors);
}
};
As you mentioned in problem, you will have to generate slug for name like one containing hyphens in place of spaces or other special characters which will be stored in model as well.

Related

Question about Model in MVC architecture in web app using NodeJs and MySQL

I am new to NodeJs and I am trying to create a web application using express framework and MySql. I get that in MVC architecture the views are for example the *.ejs files. The controllers are supposed to have the logic and the models should focus on the database.
But still I am not quite sure what is supposed to be inside the model. I have the following code in my controller (probably wrong, not following mvc design):
const mysql = require('mysql');
const db = mysql.createConnection(config);
db.query(query, (err, result) => {
if (err) {
return res.redirect('/');
}
res.render('index.ejs', {
users: result
});
});
Now from what I've read the controller should ask the model to execute the query to the database, get the results and render the view (index.ejs').
My question is this: What should be inside the model.js file? Can I make something like this?
controller.js
const db = require('./models/model.js');
db.connect();
const results = db.query(query);
if(results != null) {
res.render('index.ejs'){
users: result
});
}
model.js will make a query to mysql handle any errors and return the result.
From what I've read I have two options. Option1: pass callback function to model and let the model render the view (I think that's wrong, model should not communicate with view, or not?) Option2: possible use of async/await and wait for model to return the results but I am not sure if this is possible or how.
The model is the programmatic representation of the data stored in the database. Say I have an employees table with the following schema:
name: string
age: number
company: company_foreign_key
And another table called companies
name: string
address: string
I therefore have two models: Company and Employee.
The model's purpose is to load database data and provide a convenient programmatic interface to access and act upon this data
So, my controller might look like this:
var db = require('mongo');
var employeeName = "bob";
db.connect(function(err, connection){
const Employee = require('./models/Employee.js'); // get model class
let employeeModel = new Employee(connection); // instantiate object of model class
employee.getByName(employeeName, function(err, result){ // convenience method getByName
employee.getEmployeeCompany(result, function(err, companyResult){ // convenience method getEmployeeCompany
if(companyResultl) { // Controller now uses the results from model and passes those results to a view
res.render('index.ejs')
company: companyResult
});
})
})
}
})
Basically, the model provides a convenient interface to the raw data in the database. The model executes the queries underneath, and provides convenient methods as a public interface for the controller to access. E.g., the employee model, given an employee object, can find the employee's company by executing that query.
The above is just an example and, given more thought, a better interface could be thought up. In fact, Mongoose provides a great example of how to set up model interfaces. If you look at how Mongoose works, you can use the same principles and apply them to your custom model implementation.

How to format the received JSON to make it look beautiful?

I get an array of objects and display it on the screen, but the data is not beautiful, how to format to have indents and everything was beautiful, tell me how to do this using js or some kind of library.
const express = require('express');
const Database = require('./db');
const app = express();
const port = 3000;
const db = new Database();
app.use(express.json());
app.get('/gallery', (req, res) => {
db.pictures().then(data => {
const pictures = JSON.parse(data);
res.send(pictures)
})
});
How it looks on the screen:
Try
res.send(JSON.stringify(pictures, null, 4); // stringify with 4 spaces at each level
Taken from here
Either format from backend or from client side using JSON.stringify
JSON.stringify takes more optional arguments.
I recommend formaingt it in client side, as you can use the original JSON for rendering the page.
Human readable
Actually I am not sure why would we display data in Json format to the user, But it's your requirement, so i will roll with it.
There are useful answers and comments that gives you what you want, to display the json well formatted on the screen.
This answer intends to add bit of an edge if you want to display the json data in actual human readable form in an easy way. You can use this if you find necessary.
json.human.js
It takes json input and gives you a structured data as output. Example below:
Source
you can use JsonLint to beautify your json
https://jsonlint.com/

confusion in designing db document using mongodb

I use to design 'table' like this
teacher
- id
- name
student
- id
- teacher_id
- name
Just assume 1 teacher can have many students, so I put teacher_id to be able to do join.
But in noSql why should I do multiple document? I can put everything under user and use nested object like
user = {[
id:1,
type:teacher
student:[{
id:321
}]
]}
Imagine my app need to retrieve a list of teacher and student in different tab, still with model I can get the data I need, I just do the filter/manipulation on the client side, correct?
if you use nodejs then i preferred you is to use moongose npm on your node.It use schema and model restriction.Your approach is fine in RDBMS but in mongo you avoid to make joins.
Desgin a schema in this way that match according to your requirements and usage of data availabilty and read or write operations
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Teachers = new Schema({
//_id: ObjectId,
"name": {
"type": String,
},
"students": [{
name: String
}]
})
module.exports = mongoose.model('Teachers', Teachers);
It avoids your join.it manage all the teachers with their respective students.
You can filter on the server side and send the filtered data to client. It's more efficient.

Node.js - how to implement api/:product_url

I am new to node.js and I am using Yeoman to fetch a product details from the list of products. I am getting data from a triple store database and converting to json. The only unique ID is in the form of URL with an hash encoded at last.
In demo.controller.js:
exports.index = function(req, res) {
res.json(response);
}
In index.js:
var controller = require('./demo.controller');
var router = express.Router();
router.get('/', controller.index);
module.exports = router;
Here, response output is the following json structure for URL/api/product_list:
[{
"url":"http://example.com/demo/32b9jkd902n2"
"product":"stack",
"name":"test",
"price":"233"
}, {
"url":"http://example.com/demo/5nf9djdkdks0"
"product":"flow",
"name":"dev",
"price":"433"
}]
Now, I want to get details of each product. something like URL/api/product_list/:product_url ?
Basically, when I access url with product_url from the list of products, I should be able to get the product details.
Can someone help me in implementing the URL with /api/product_list/:product_url with an output of single product?
So, as there is no other unique identifier besides the URL you could to one of the following:
Parse unique ID from the url to /api/product_list as id to the output and use that - /api/product_list/:id
Generate a hash (md5, sha1) of the whole url to the output as id and use that as an unique id.
Note: never used Yeoman before, so I don't know if you have some limitations from there.
I think /api/product_list/:url will not fly, and if it does, it sure will look ugly.

Chaining models relations in node.js (node-orm) like in Rails

I'm building a relatively big NodeJS application, and I'm currently trying to figure out how to fetch the data I need from the DB. Here is a part of my models :
One user has one role, which has access to many modules (where there's a table role_modules to link roles and modules).
In Rails, I would do something like user.role.modules to retrieve the list of the modules he has access to. In NodeJS it's a bit more complicated. I'm using node-orm2 along with PostgreSQL. Here is what I have so far:
req.models.user.find({email: req.body.user}, function(err, user) {
user[0].getRole(function(err, role) {
role.getModules(function(err, modules) {
var list_modules = Array();
modules.forEach(function(item) {
console.log(item);
list_modules.push(item.name);
})
But I can't do this, because item only contains role_id and module_id. If I want to have the name, I would have to do item.getModule(function() {...}), but the results would be asynchronous ... so I don't really see how I could end up with an array containing the names of the modules a user has access to ... have any idea?
Also, isn't that much slower than actually running a single SQL query with many JOIN? Because as I see it, the ORM makes multiple queries to get the data I want here...
Thank you!
I wrote an ORM called bookshelf.js that aims to simplify associations and eager loading relations between models in SQL. This is what your query would probably look like to load the role & modules on a user given your description:
var Module = Bookshelf.Model.extend({
tableName: 'modules'
});
var Role = Bookshelf.Model.extend({
tableName: 'roles',
modules: function() {
return this.belongsToMany(Module);
}
});
var User = Bookshelf.Model.extend({
tableName: 'users'
role: function() {
return this.hasOne(Role);
}
});
User.forge({email: req.body.user})
.fetch({
require: true,
withRelated: ['role.modules']
})
.then(function(user) {
// user, now has the role and associated modules eager loaded
console.log(user.related('role'));
console.log(user.related('role').related('modules'))
}, function(err) {
// gets here if no user was found.
});
Might be worth taking a look at.

Categories

Resources