I followed this tutorial to create a restful api with npm and postgres
Designing a RESTful API With Node and Postgres
I got everything to work without a problem, closed the server and went to other things.. when I got back, the routing stopped working suddenly giving 404 error.. I checked everything related to routing and I can't find the problem!
When I connect to localhost:3000 I get the correct express homepage
but when I try to access the api, localhost:3000/api/patients the 404 error page appears
Here is my code
index.js
var express = require('express');
var router = express.Router();
var db = require('../queries');
router.get('/api/patients', db.getAllPatients);
module.exports = router;
queries.js
var promise = require('bluebird');
var options = {
// Initialization Options
promiseLib: promise
};
var pgp = require('pg-promise')(options);
var connectionString = 'postgres://localhost:5432/maindb'
var db = pgp(connectionString);
module.exports = {
getAllPatients: getAllPatients
}
function getAllPatients(req, res, next) {
db.any('select * from patients where deleted = false')
.then(function (data) {
res.status(200)
.json({
status: 'success',
data: data,
message: 'Retrieved ALL patients'
});
})
.catch(function (err) {
return next(err);
});
}
It seems something was wrong with express installation, something corrupted it.
I recreated the project from point zero and specified which version of express and bluebird to install, and everything seems to work fine without any problems.
Related
I followed the instructions for connecting cassandraDB and expressJs (V4.16.1) here -> https://expressjs.com/en/guide/database-integration.html#cassandra
but I am having this error in the browser:
msg
name "ArgumentError"
info "Represents an error that is raised when one of the arguments provided to a method is not valid."
message "'localDataCenter' is not defined in Client options and also was not specified in constructor. At least one is required. Available DCs are: [datacenter1]"
code index.js
var express = require('express');
var router = express.Router();
var cassandra = require('cassandra-driver');
var client = new cassandra.Client({ contactPoints:['localhost'] });
client.connect(function(err, result){
console.log('cassandra connection done');
});
var getAlllogs = 'SELECT * FROM logs.ourlogs';
/* GET home page. */
router.get('/', function(req, res, next) {
//res.render('index', { title: 'Express' });
client.execute(getAlllogs,[], function(err, result){
if(err){
res.status(404).send({msg:err});
} else{
res.render('index', {
ourlogs: result.rows[0]
})
}
});
});
module.exports = router;
code index.jade
extends layout
block content
h1 all logs
ul
each ourlog, i in ourlogs
li #{ourlog.curTime}
cassandra info
Connected to Test Cluster at 127.0.0.1:9042.
[cqlsh 5.0.1 | Cassandra 3.11.6 | CQL spec 3.4.4 | Native protocol v4]
Use HELP for help.
cqlsh>
what did I miss?
This is the answer :
var client = new cassandra.Client({ contactPoints: ['127.0.0.1'], localDataCenter: 'datacenter1', keyspace: 'yourkeyspacename' });
ExpressJS team should edit it on their website
I have a very simple user backend up and running (node, express, mongoose, mongo, etc) and with postman can verify when I add a user it works, when I request a login it works and get a token, and if I put in the wrong details it rejects it,
Now I used this git hub repo https://github.com/christiannwamba/vue-auth-vuex to spin up a simple frontend for this. Which I thought was all working fine as it appeared to be logging in until I found it was accepting whatever details I put in for the email and password as correct!
The backend server kept responding ok when I hit it with the vue app, but on closer inspection when I console logged what it was getting, which was null and returning user not found. So again I don't think there is anything wrong here.
Something I have noticed in chrome dev tools network, it is sending two versions of authenticate, first is empty and then the next one has responses.
I'm at a bit of a loss why it's sending empty requests first time and why it allows the login when it's getting a bad return.
Server.js file:
const express = require('express');
const logger = require('morgan');
const movies = require('./routes/movies') ;
const users = require('./routes/users');
const bodyParser = require('body-parser');
const mongoose = require('./config/database'); //database configuration
var jwt = require('jsonwebtoken');
var cors = require('cors')
const app = express();
// Add cors
app.use(cors());
app.options('*', cors()); // enable pre-flight
app.set('secretKey', 'nodeRestApi'); // jwt secret token
// connection to mongodb
mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection error:'));
app.use(logger('dev'));
app.use(bodyParser.urlencoded({extended: false}));
app.get('/', function(req, res){
res.json({"api" : "User API"});
});
// public route
app.use('/users', users);
// private route
app.use('/movies', validateUser, movies);
app.get('/favicon.ico', function(req, res) {
res.sendStatus(204);
});
function validateUser(req, res, next) {
jwt.verify(req.headers['x-access-token'], req.app.get('secretKey'), function(err, decoded) {
if (err) {
res.json({status:"error", message: err.message, data:null});
}else{
// add user id to request
req.body.userId = decoded.id;
next();
}
});
}
// express doesn't consider not found 404 as an error so we need to handle 404 it explicitly
// handle 404 error
app.use(function(req, res, next) {
let err = new Error('Not Found');
err.status = 404;
next(err);
});
// handle errors
app.use(function(err, req, res, next) {
console.log(err);
if(err.status === 404)
res.status(404).json({message: "Not found"});
else
res.status(500).json({message: "Something looks wrong :( !!!"});
});
app.listen(3000, function(){
console.log('Node server listening on port 3000');
});
Update:
I have added in under my CORS bit in server.js:
app.options('/users/authenticate', function(req, res){
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'POST');
res.end();
});
In network I now only get the one request. Under form data it appears to be there but it's saying in the response that data is null, and even more odd the vuejs is still logging in and allowing access to the restricted pages.
Temporarily comment out the line where you set the headers to application/x-www-form-urlencoded. Then add app.use(bodyParser.json()) to your server.js and see if it works. What's happening is your request object is malformed, which is why the server cannot parse the request correctly.
Looks like CORS issue. If you run UI using a different server and your backend is running by itself, then your browser will send pre-flight request first which is an options request. That is the reason you see 2 authenticate requests in the developer tools. You can read more about this here
Why is an OPTIONS request sent and can I disable it?
I am using an Angular front-end with a Nodejs backend. Im currently proxying all my front-end requests through my express server. However when I make my http request to the Here API I am rejected due to an invalid combination of app_id and app_code.
angular service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http'
import { HttpParams } from '#angular/common/http'
#Injectable({
providedIn: 'root'
})
export class GetReqPlaces {
constructor(private http: HttpClient) { }
getPlaces(wLong,sLat,eLong,nLat){
// let obj = {params: {westLong: wLong, southLat: sLat, eastLong:eLong, northLat:nLat }};
let params = new HttpParams().set("westLong" , '-97.783').set("southLat", '30.231').set( "eastLong" , '-97.740').set("northLat", '30.329');
return this.http.get( 'api/find/places', { params : params}).subscribe(res=>console.log(res))
}
}
server.js
const express = require("express")
const bodyParser = require("body-parser")
const cors = require("cors")
const path = require("path")
const app = express();
const request = require("request")
const environment= require('./keys')
app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
let reqPath = __dirname.substring(0,__dirname.length-7)
app.use(express.static(path.join(reqPath, '/dist/angular-places-search')));
app.get('/api/find/places', (req, res) => {
let appId = environment.environment.appId;
let appCode = environment.environment.appCode;
let URL= `https://places.cit.api.here.com/places/v1/discover/search?app_id={${appId}}&app_code={${appCode}}&in=${req.query.westLong},${req.query.southLat},${req.query.eastLong},${req.query.northLat}&pretty`;
console.log(URL)
request(URL, function (error, response, body) {
let data={
body:body,
};
console.log(error,response)
res.send(data);
});
});
app.get('/test', (req, res) => res.send('Well this route was a hit! Bada....tsss'));
// CATCH ALL
app.get('*', (req, res) => {
res.sendFile(path.join(reqPath, 'dist/angular-places-search/index.html'));
});
app.listen(4000, () => console.log(`Express server running on port 4000`));
Before this I was running into CORS and request issues but I think I sorted those out. Based on my research on this same error code (In the context of the framework that Im working in), people overwhelmingly suggest to wait for tokens to register with Here API. Waiting two days is enough I think, still doesnt work. Then there is the very popular solution of just scratching the Here freemium and starting a new project, which I did, and which did not solve my issue. Very few things I have 100% certainty on but I did copy my keys correctly and the URL path built is according to the required Here syntax.
If anyone has any insight you will be my Hero, and also the catalyst for my continued learning :D. Happy Sunday!
In addition the incoming message I get through express is :
method: 'GET',
path: '/places/v1/discover/search?app_id=%notmyid%7D&app_code=%normycode%7D&in=-97.783,30.231,-97.740,30.329&pretty'
However i dont know why it is setting the app_id=% instead of using {}, when i console log the URL it is correct, with my app_id and app_code
The %7D is the url encoded value of the symbol } (urlencoding) which is done by most libraries. For using the HERE API you should not enclose the app_id/app_code between {}. They should be provided directly as strings, check the examples
I am trying to authenticate using passport-jwt, express etc.
I currently am not getting authenticated, but most troubling is that I cant seem to console.log inside the function... nothing happens.
Here is the full passport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/user');
const config = require('../config/database');
module.exports = function(passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromHeader();
opts.secretOrKey = config.secret;
console.log(opts); // THIS SHOW PRIORE TO THE EXPRESS LISTEN MSG!
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
console.log(jwt_payload); // THIS DOESNT EVER SHOW!
User.getUserById(jwt_payload._id, (err, user) => {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
// or create a new account
}
});
}));
}
the users.js
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const config = require('../config/database');
const User = require('../models/user');
...code removed for brevity...
router.get('/profile', passport.authenticate('jwt', {session:false}), (req,res,next) => {
res.json({user: req.user});
});
module.exports = router;
Any ideas on how I can 'see' my payload so I can find whats going on?
Im using postman to hit 'http://localhost:3000/users/profile'
passing headers "authentication" and the token "JWT my-token..."
THx - I realize this is a bit of an odd request...The tutorial I was following, indicates I should be able to see the payload, even if i am not authenticated...
So Im kinda stuck with no idea where to turn.
UGH - this is the second time I've continued to plug away at something after I asked, only to hit upon a solution. apperantly the version Im am using is newer than the examples and ExtractJwt.fromHeader() should be .fromAuthHeaderWithScheme("jwt"). It is frustrating that it did not error, and did not progress enough to log, however - NOW I get my console log, and can see my payload... I hope this helps someone else. (I am frustrated with the pace and changes made to js libs, that destroy backwards compatibility, and do not clearly document a path, I understand kinda why it happens, but it makes learning and troubleshooting very frustrating)
This route redirect is doing exactly what I want in Chrome and Firefox, but for some reason, it is not working in Safari in the production environment:
var express = require('express');
var router = express.Router();
var pool = require('../modules/pg-pool');
router.get('/', async (req, res) => {
var client = await pool.connect();
try {
var stripeConnectState = req.query.state;
var stripeConnectVendorIdResult = await client.query('SELECT vendor_id ' +
'FROM users_vendors ' +
'WHERE stripe_connect_state=$1;',
[stripeConnectState])
client.release();
if (stripeConnectVendorIdResult.rows[0] && stripeConnectVendorIdResult.rows[0].vendor_id) {
var stripeConnectVendorId = stripeConnectVendorIdResult.rows[0].vendor_id;
}
if (stripeConnectVendorId) {
var redirectUrl = [req.protocol, '://', req.get('Host'), '/#/account/vendor/details/', stripeConnectVendorId, '?', req.originalUrl.split("?").pop()].join('');
res.redirect(redirectUrl);
} else {
console.log('There was no vendor id to match the stripe state received');
res.sendStatus(403);
}
} catch (e) {
console.log('Error vendor id GET SQL query task', e);
res.sendStatus(500);
}
});
module.exports = router;
The craziest part to me is that when I I tested locally in Safari, it worked! For some reason, when I deploy to Heroku, it no longer works in production for Safari.
When I test locally, it is redirecting to
http://localhost:5000/#/account/vendor/details/7?state=XXXXXXXXXXXXXXXXXXXXXXXX&scope=read_write&code=ac_XXXXXXXXXXXXXXXXXX
But in production, it is redirecting to the base url
https://www.fairlywed.com/
without the incredibly important query parameters sent along by Stripe. This almost seems like I have a race condition on my hands that never triggers in Chrome or Firefox, but always triggers in Safari.
I'm also wondering if my use of angular-ui-router or the fact that stripe is directing to my site might be involved, but none of those really make a whole lot of sense to me. I might just be grasping at straws at this point.
I found a band-aid fix, but I'm still not sure why this is happening. On production, the req.protocol was coming in as http which is handled by another part of my code:
function redirectChecker (req, res, next) {
if (env === 'production') {
if (req.headers['x-forwarded-proto'] !== 'https') {
var urlAfterConversion = ['https://', req.get('Host'), req.url].join('');
return res.redirect(urlAfterConversion);
}
}
return next();
};
For some reason on Safari, that redirect doesn't work (maybe Safari doesn't accept a redirect followed by a redirect?)
So it failed and redirected to my landing page. I'm still not sure why it solved the problem, but this code is working:
if (stripeConnectVendorId) {
var env = process.env.NODE_ENV || 'development';
var redirectUrl = [req.protocol, '://', req.get('Host'), '/#/account/vendor/details/', stripeConnectVendorId, '?', req.originalUrl.split("?").pop()].join('');
if (env === 'production') {
redirectUrl = 'https:' + redirectUrl.split(':')[1];
}
res.redirect(redirectUrl);
} else {
console.log('There was no vendor id to match the stripe state received');
res.sendStatus(403);
}