Struggling to get my head around this for a week and a half, I was wondering how to get a .json endpoint that is from a query from the Sequelize ORM. Currently it logs a 404 error "GET /api/users 404 3ms". As you may have heard the documentation for Sequelize is pretty limited and I've been searching github repo after tutorial and none have worked thus far, so I'd thought I'd ask here.
A small excerpt (code on https://github.com/NatuMyers/A.M.E.N.SQL-Stack):
// VARS -----------------------------
var express = require('express')
, bodyParser = require('body-parser')
, errorHandler = require('errorhandler')
, methodOverride = require('method-override')
, morgan = require('morgan')
, http = require('http')
, path = require('path')
, db = require('./models')
var router = require('express').Router();
var app = express()
// all environments
app.set('port', process.env.PORT || 3000)
app.set('views', __dirname + '/views')
app.set('view engine', 'jade')
app.use(morgan('dev'))
app.use(bodyParser())
app.use(methodOverride())
app.use(express.static(path.join(__dirname, 'public')))
// SEQUELIZE MODELS
var userVar = require('./models/user');
// dev only
if ('development' === app.get('env')) {
app.use(errorHandler())
}
// Make db, and make it listen
db
.sequelize
.sync()
.complete(function(err) {
if (err) {
throw err
} else {
http.createServer(app).listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'))
})
}
})
// HTTP GET endpoints
module.exports = function() {
router.get('/', function(req, res, next){
res.json({ message: 'This works at localhost:3000/api but getting a list of users is a pain :(' });
});
// question
router.get('/users', function(req, res, next){
res.json(/* I need to make sequelize send a part of the User db here. */);
});
return router;
};
I moved on from this by using Epilogue.js (in a vanilla way).
I added models INLINE with Sequelize (I wasted lots of time trying to import models), then add any middle ware and create the restful api based on the syntax below.
// 1. ADD SEQUELIZE MODELS ---- ---- ---- ----
var database = new Sequelize('raptroopdb', 'root', 'strongpassword');
var Employee = database.define('Employee', {
name: Sequelize.STRING,
hireDate: Sequelize.DATE
});
// Add Account model with foreign key constraint to Employee
var Account = database.define('Account', {
name: Sequelize.STRING,
managerId: {
type: Sequelize.INTEGER,
references: {
// This is a reference to model Employee
model: Employee,
// This is the column name of the referenced model
key: 'id',
}
}
});
// 2. ROOM FOR MIDDLEWARE to use for all requests
router.use(function(req, res, next) {
// do logging
console.log('In server.js');
// make sure we go to the next routes and don't stop here
next();
});
// Initialize epilogue
epilogue.initialize({
app: app,
sequelize: database
});
app.use(express.static(__dirname + "/public"));
app.get('/', function(req, res) {
res.redirect('/public/index.html');
});
// 3. Create REST resource
var employeeResource = epilogue.resource({
model: Employee,
endpoints: ['/api/employees', '/api/employees/:id']
});
var acctResource = epilogue.resource({
model: Account,
endpoints: ['/api/accounts', '/api/accounts/:id']
});
// Create database and listen
database
.sync({
force: false
})
.then(function() {
app.listen(port, function() {
console.log('listening at %s', port);
});
});
Related
I am learning to use mongoDB AND ExpressJS by building a Rest API that I would use with ReactJS.
I have always chosen MySQL for the management of my database, but the mongoDB database is not relational and it is still difficult for me to understand.
An example of what I want to do
Let's say that I have created a blog and want to get all the articles from a user logged in with an account.
All these operations are managed with a REST API and MongoDB.
How to create a OneToMany relationship between articles and a user.
With MySQL I just had to specify a user_id key for each article in an article table.
But with mongoDB how to create this and especially for a user who is logged in with an account, so that only a logged in user can view his articles.
EDIT
I have tried something, it works but I don't know if it's the right approach.
Context:
I made a REST API with NodeJS and ExpressJS.
The API will allow a user to organize their applications to facilitate the search for a job.
A user must create an account and log in to take advantage of all of the application's features, so no information is publicly available.
For registration and authentication of a user, I use PassportJS, mongoConnect and ExpressSession
To start, the User model of mongoDB
const userSchema = mongoose.Schema({
name: {
type:String
},
email: {
type:String,
required:true,
unique:true
},
email_is_verified: {
type:Boolean,
default:false
},
password: {
type:String,
},
referral_code : {
type:String,
default: function() {
let hash = 0;
for(let i=0; i < this.email.length; i++){
hash = this.email.charCodeAt(i) + ((hash << 5) - hash);
}
let res = (hash & 0x00ffffff).toString(16).toUpperCase();
return "00000".substring(0, 6 - res.length) + res;
}
},
referred_by : {
type: String,
default:null
},
third_party_auth: [ThirdPartyProviderSchema],
date: {
type:Date,
default: Date.now
}
},
{ strict: false }
);
module.exports = mongoose.model('Users', userSchema);
The Apply model represents an apply for a job, for now there is only the title.
To create the OneToMany relationship, I add a User field which refers to my User model
Function to retrieve all applies, so I retrieve the user id of the session.
const applySchema = mongoose.Schema({
title: { type:String, required:true },
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
})
module.exports = mongoose.model('Apply', applySchema);
I created a controller for the management of a user's applies
exports.getAllApplies = (req, res, next) => {
res.locals.currentUser = req.user;
const userId = res.locals.currentUser.id
Apply.find({ user:userId })
.then(applies => res.status(200).json({ message:'success',
applies:applies }))
.catch(error => res.status(400).json({ error:error, message: 'Failed'}))
}
Function allowing to consult an apply
exports.getOneApply = (req, res, next) => {
res.locals.currentUser = req.user;
const userId = res.locals.currentUser.id
Apply.findOne({ _id:req.params.id, user:userId })
.then(apply => res.status(200).json({ message: `Apply with id
${apply._id} success`, apply:apply}))
.catch(error => res.status(500).json({ error:error, message:'Failed'}))
}
The routes of my api, I add an auth middleware to allow requests only for a user with a token
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const applyCtrl = require('../controllers/apply');
router.get('/', auth, applyCtrl.getAllApplies);
router.get('/:id', auth, applyCtrl.getOneApply);
module.exports = router;
I apologize for the length of the post, if you have any questions, I would be happy to answer them.
Thank you in advance for your help and your answers.
I'm learning expressjs and i'm trying to update a document in my mongodb database by id and using PUT to specify a route, after entering this command in the terminal:
curl -X PUT --data "name =James&age = 20&nationality=American"http://localhost:3000/people/5d3ba2a863ba682d70242131
It prints out the document but nothing has been updated
I have used {new:true} as recommended against the default setting ({new:false}) however nothing has changed. without specifying any route it works
e.g
Person.findOneAndUpdate({name:'deean'},{age:34},function(err,response){
console.log(response);
});
the above code works
but this doesn't:
app.put('/people/:id', function(req, res){
Person.findByIdAndUpdate(req.params.id, req.body, {new: true} ,function(err, response){
if(err)
res.json({message:"Error in updating person with id" + req.params.id});
res.json(response);
});
});
app.listen(3000);
the original document i am trying to change is
{ _id: 5d3ba2a863ba682d70242131,
name: 'deean ajashi',
age: 34,
nationality: 'indian',
__v: 0 } ]
this is the output i'm getting on the terminal
C:\Users\Diola>curl -X PUT --data "name = James&age = 20&nationality = American" http://localhost:3000/people/5d3ba2a863ba682d70242131
{"_id":"5d3ba2a863ba682d70242131","name":"dean ajashi","age":34,"nationality":"indian","__v":0}
C:\Users\Diola>
The format i'm using is from Tutorialspointclick to check out page on Tutorialspoint
edit: i think this is what you mean by model and router file though i'm not sure
var express = require('express');
var multer = require("multer");
var upload = multer();
var app = express();
app.set('view engine', 'pug');
app.set('views', './views');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(upload.array());
app.use(express.static('public'));
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/mydb', { useNewUrlParser: true });
var personSchema = mongoose.Schema({
name: String,
age: Number,
nationality: String
});
var Person = mongoose.model("Person", personSchema);
app.get('/person', function(req, res){
res.render('person');
});
app.post('/person', function(req, res){
var personInfo = req.body;
console.log('recived your request');
console.log(req.body);
if(!personInfo.name || !personInfo.age || !personInfo.nationality){
res.render('show_message', {
message: "Sorry, you provided worng info", type: "error"});
} else {
var newPerson = new Person({
name: personInfo.name,
age: personInfo.age,
nationality: personInfo.nationality
});
newPerson.save(function(err, Person){
if(err)
res.render('show_message', {message: "Database error", type: "error"});
else
res.render('show_message', {
message: "New person added", type: "success", person: personInfo});
});
}
});
when you pass {new: true} it returns the updated document (Vs) old document if it's false it has nothing to do with update operation, Can you give us your model and router file, meanwhile - try below code & check if you're able to get request by printing req.params.id and req.body :
app.put('/people/:id', function(req, res){
Person.findByIdAndUpdate(req.params.id, {$set:req.body}, {new: true} ,function(err, response){
if(err) res.json({message: "Error in deleting record id " + req.params.id});
else res.json(response);
});
});
app.listen(3000);
I am building a content management system for an art portfolio app, with React. The client will POST to the API which uses Mongoose to insert into a MongoDB. The API then queries the DB for the newly inserted image, and returns it to the client.
Here's my code to connect to MongoDB using Mongoose:
mongoose.connect('mongodb://localhost/test').then(() =>
console.log('connected to db')).catch(err => console.log(err))
mongoose.Promise = global.Promise
const db = mongoose.connection
db.on('error', console.error.bind(console, 'MongoDB connection error:'))
const Schema = mongoose.Schema;
const ImgSchema = new Schema({
img: { data: Buffer, contentType: String }
})
const Img = mongoose.model('Img', ImgSchema)
I am using multer and fs to handle the image file. My POST endpoint looks like this:
router.post('/', upload.single('image'), (req, res) => {
if (!req.file) {
res.send('no file')
} else {
const imgItem = new Img()
imgItem.img.data = fs.readFileSync(req.file.path)
imgItem.contentType = 'image/png'
imgItem
.save()
.then(data =>
Img.findById(data, (err, findImg) => {
console.log(findImg.img)
fs.writeFileSync('api/uploads/image.png', findImg.img.data)
res.sendFile(__dirname + '/uploads/image.png')
}))
}
})
I can see in the file structure that writeFileSync is writing the image to the disk. res.sendFile grabs it and sends it down to the client.
Client side code looks like this:
handleSubmit = e => {
e.preventDefault()
const img = new FormData()
img.append('image', this.state.file, this.state.file.name)
axios
.post('http://localhost:8000/api/gallery', img, {
onUploadProgress: progressEvent => {
console.log(progressEvent.loaded / progressEvent.total)
}
})
.then(res => {
console.log('responsed')
console.log(res)
const returnedFile = new File([res.data], 'image.png', { type: 'image/png' })
const reader = new FileReader()
reader.onloadend = () => {
this.setState({ returnedFile, returned: reader.result })
}
reader.readAsDataURL(returnedFile)
})
.catch(err => console.log(err))
}
This does successfully place both the returned file and the img data url on state. However, in my application, the image always displays broken.
Here's some screenshots:
How to fix this?
Avoid sending back base64 encoded images (multiple images + large files + large encoded strings = very slow performance). I'd highly recommend creating a microservice that only handles image uploads and any other image related get/post/put/delete requests. Separate it from your main application.
For example:
I use multer to create an image buffer
Then use sharp or fs to save the image (depending upon file type)
Then I send the filepath to my controller to be saved to my DB
Then, the front-end does a GET request when it tries to access: http://localhost:4000/uploads/timestamp-randomstring-originalname.fileext
In simple terms, my microservice acts like a CDN solely for images.
For example, a user sends a post request to http://localhost:4000/api/avatar/create with some FormData:
It first passes through some Express middlewares:
libs/middlewares.js
...
app.use(cors({credentials: true, origin: "http://localhost:3000" })) // allows receiving of cookies from front-end
app.use(morgan(`tiny`)); // logging framework
app.use(multer({
limits: {
fileSize: 10240000,
files: 1,
fields: 1
},
fileFilter: (req, file, next) => {
if (!/\.(jpe?g|png|gif|bmp)$/i.test(file.originalname)) {
req.err = `That file extension is not accepted!`
next(null, false)
}
next(null, true);
}
}).single(`file`))
app.use(bodyParser.json()); // parses header requests (req.body)
app.use(bodyParser.urlencoded({ limit: `10mb`, extended: true })); // allows objects and arrays to be URL-encoded
...etc
Then, hits the avatars route:
routes/avatars.js
app.post(`/api/avatar/create`, requireAuth, saveImage, create);
It then passes through some user authentication, then goes through my saveImage middleware:
services/saveImage.js
const createRandomString = require('../shared/helpers');
const fs = require("fs");
const sharp = require("sharp");
const randomString = createRandomString();
if (req.err || !req.file) {
return res.status(500).json({ err: req.err || `Unable to locate the requested file to be saved` })
next();
}
const filename = `${Date.now()}-${randomString}-${req.file.originalname}`;
const filepath = `uploads/${filename}`;
const setFilePath = () => { req.file.path = filepath; return next();}
(/\.(gif|bmp)$/i.test(req.file.originalname))
? fs.writeFile(filepath, req.file.buffer, (err) => {
if (err) {
return res.status(500).json({ err: `There was a problem saving the image.`});
next();
}
setFilePath();
})
: sharp(req.file.buffer).resize(256, 256).max().withoutEnlargement().toFile(filepath).then(() => setFilePath())
If the file is saved, it then sends a req.file.path to my create controller. This gets saved to my DB as a file path and as an image path (the avatarFilePath or /uploads/imagefile.ext is saved for removal purposes and the avatarURL or [http://localhost:4000]/uploads/imagefile.ext is saved and used for the front-end GET request):
controllers/avatars.js (I'm using Postgres, but you can substitute for Mongo)
create: async (req, res, done) => {
try {
const avatarurl = `${apiURL}/${req.file.path}`;
await db.result("INSERT INTO avatars(userid, avatarURL, avatarFilePath) VALUES ($1, $2, $3)", [req.session.id, avatarurl, req.file.path]);
res.status(201).json({ avatarurl });
} catch (err) { return res.status(500).json({ err: err.toString() }); done();
}
Then when the front-end tries to access the uploads folder via <img src={avatarURL} alt="image" /> or <img src="[http://localhost:4000]/uploads/imagefile.ext" alt="image" />, it gets served up by the microservice:
libs/server.js
const express = require("express");
const path = app.get("path");
const PORT = 4000;
//============================================================//
// EXPRESS SERVE AVATAR IMAGES
//============================================================//
app.use(`/uploads`, express.static(`uploads`));
//============================================================//
/* CREATE EXPRESS SERVER */
//============================================================//
app.listen(PORT);
What it looks when logging requests:
19:17:54 INSERT INTO avatars(userid, avatarURL, avatarFilePath) VALUES ('08861626-b6d0-11e8-9047-672b670fe126', 'http://localhost:4000/uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png', 'uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png')
POST /api/avatar/create 201 109 - 61.614 ms
GET /uploads/1536891474536-k9c7OdimjEWYXbjTIs9J4S3lh2ldrzV8-android.png 200 3027 - 3.877 ms
What the user sees upon successful GET request:
I am currently doing a React, Express, Massivejs, postgreSql app. I am getting the error "TypeError: db.createList is not a function" anytime I'm trying to hit my post endpoint. I'm not sure how to remedy it since it looks correct.
My file structure:
My server file looks like this:
var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var massive = require("massive");
var connectionString = 'postgress://LonnieMcGill#localhost/todo';
var massiveInstance = massive.connectSync({connectionString : connectionString})
var config = require('./config.js');
var app = module.exports = express();
app.set('db', massiveInstance);
var listCtrl = require('./controller/listCtrl.js');
// **************** Middleware ****************
app.use(express.static(__dirname + './../public'));
app.use(bodyParser.json());
// ****************** Endpoints ***************
app.post('/api/add/list', listCtrl.createList);
app.get('/api/get/list', listCtrl.createList);
app.get('*', function(req, res) {
res.sendFile('index.html', { root: './public'});
})
app.listen(config.port, function() { console.log('Server initiated on port', config.port); });
My controller looks like this:
var app = require('../server.js');
var db = app.get('db');
module.exports = {
createList: function(req, res, next) {
console.log('db'my);
db.createList([req.body.name], function(err, res) {
res.status(200).send('List created');
})
},
readList: function(req, res) {
db.readList(function(err, res) {
if (err) {
console.log("readList failed");
} else {
console.log("readList working " + req.body, req.params);
}
})
}
}
My createList.sql file looks like this:
INSERT INTO list (
name
)
VALUES (
$1
);
The documentation clarifies this issue. By default, the "db" folder should stay in the root directory of your project, and not where the scripts consuming the database are (in this case, "server/").
You must either move "db" to the root directory of your project (so as to be alongside "server", "public", etc.), or configure the scripts property to point to that location:
var massiveInstance = massive.connectSync({
scripts: "server/db",
connectionString
})
I've created a very simple blog web app with ExpressJS and MongoDB. But the index page doesn't render the 'blogPosts' that I input into the DB. It only shows the title as "BL's Blog", without any posts below.
Why aren't the posts showing?
app.js:
//Module dependencies
var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');
//Mongodb
var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:27017/hello-express/');
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
//GET
app.get('/', routes.index(db));
app.get('/users', user.list);
app.get('/userlist', routes.userlist(db));
app.get('/newuser', routes.newuser);
//POST
app.post('/adduser', routes.adduser(db));
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
routes/index.js(I only included exports.index as it's the relevant route to this problem):
exports.index = function(db) {
return function(req, res) {
var posts = db.get('blogPosts');
posts.find({}, {}, function(e, docs) {
res.render('index', {
"index": docs
});
});
};
};
views/index.jade:
extends layout
block content
h1.
BL's Blog
ul
- each post in index
li
h3 = post.title
p = post.content
Ok now I have actually found out the answer to my own question, thanks to the help of WiredPrairie.
The first problem lies with the name of the database that I'm using. Thus in my index.js, it should have been:
exports.index = function(db) {
return function(req, res) {
var posts = db.get('hello-express'); //Here's the difference.
posts.find({}, {}, function(e, docs) {
res.render('index', {
"index": docs
});
});
};
};
I listed in app.js that I'm using the database called 'hello-express' with this line below:
var db = monk('localhost:27017/hello-express/');
Hence the name of database being used should be named as 'hello-express'.
However, even after doing so, I could not render the Jade page properly. This was due to a syntax error (I know, I'm new.)
extends layout
block content
h1.
BL's Blog
ul
- each post in index
li
h3 #{post.title}
p #{post.content}
By using #{<var>} instead of =, the page was able to render properly. I still have no idea why this is the case, but at least it has solved my problem for now.