Express not finding route - node.js

I have the following routes defined in Angular:
export class CartService {
private cartAddUrl = "/api/cart/add";
private cartGetUrl = "/api/cart/get";
private cartCountUrl = "/api/cart/count";
}
Calls to the Node server using the first two variables work as expected. Calls using the cartCountUrl result in a 404 error, the route is not found.
This is the function that uses the cartCountUrl:
public getNumberOfItems() {
return this.httpClient.get<number>(this.cartCountUrl)
.pipe(
tap(count => console.log('count', count)),
map(count => this.numberOfItems = count)
)
}
The routes are defined on Node as follow:
'use strict';
var ctrlCart = require('../controllers/cart.server.controller');
var ctrlUser = require('../controllers/user.server.controller');
module.exports = (app) => {
app.route('/api/cart/add')
.post(ctrlUser.authenticate, ctrlCart.cartAdd);
app.route('/api/cart/get')
.get(ctrlUser.authenticate, ctrlCart.cartGet);
app.route('/api/cart/count')
.get(ctrlUser.authenticate, ctrlCart.cartCount);
}
Again, the first two routes are found. The '/api/cart/count' route results in 404 error.
cart.server.controller:
exports.cartCount = function(req, res) {
Cart.findOne({pasword: req.password})
.exec(function(err, cart) {
if(err) {
console.log('error get cart count');
res.status(500).json(err);
return;
} else if(!cart) {
res.status(404).json(0);
} else {
console.log('cart count', cart.cartItem.length);
res.status(200).json(cart.cartItem.length);
return;
}
})
}

Simple typo. In the controller, "password" was spelled with one 's'.
Cart.findOne({pasword: req.password})
I don't understand why node would report this as a routing error.

Related

Dependecy Injection using Class into Express

I'm using Express into a TypeScript project and I have the following situation
This is my route file
...
import findAllUsersFactory from "src/factory/FindAllUsers";
routes.get("/users", findAllUsersFactory().handle);
...
This is the factory where I do a sequence of injections
const findAllUsersFactory = () => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController;
};
This is my Controller
class FindAllUsersController {
constructor(private findUserBusiness: FindAllUsersBusiness) { }
async handle(request: Request, response: Response) {
const allUsers = await this.findUserBusiness.execute();
return response.status(200).send({ allUsers });
}
}
And finally my Business
class FindAllUsersBusiness {
constructor(private usersRepository: IUsersRepository) {}
async execute() {
return this.usersRepository.findAll();
}
}
The problem is that I'm getting an error "Cannot read property 'execute' of undefined" because the findUserBusiness into handle function is undefined. And what I can't understand is that if I change my route to
routes.get("/users", (request, response) => {
findAllUsersFactory().handle(request, response);
});
it works
I've tried to log the functions, but I can say why findUserBusiness is undefined since it came from the constructor, and since the handle functions came from an instance of FindAllUsersController it should have it "defined"
You need to make some adjustments in order to adapt your factory to the way router.get expects its parameters.
const findAllUsersFactory = (req, res) => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController.handle(req, res)
};
Then in your router you need to do the following:
routes.get("/users", findAllUsersFactory);

Why Hook is called in all update services methods

I'm create a hook file with the following information, which is Hooks.js
Hooks.js is working to authenticate an actions with JWT when need it, I dont need it in all servies calls.
As my understanding the syntax to call a hook was app/use route/hooks and those hooks were only applied to and specific route and not globally.
module.exports = {
errorHandler: (context) => {
if (context.error) {
context.error.stack = null;
return context;
}
},
isValidToken: (context) => {
const token = context.params.headers.authorization;
const payload = Auth.validateToken(token);
console.log(payload);
if(payload !== "Invalid" && payload !== "No Token Provided"){
context.data = payload._id;
}
else {
throw new errors.NotAuthenticated('Authentication Error Token');
}
},
isValidDomain: (context) => {
if (
config.DOMAINS_WHITE_LIST.includes(
context.params.headers.origin || context.params.headers.host
)
) {
return context;
}
throw new errors.NotAuthenticated("Not Authenticated Domain");
},
normalizedId: (context) => {
context.id = context.id || context.params.route.id;
},
normalizedCode: (context) => {
context.id = context.params.route.code;
},
};
Then I create a file for services and routes, like the following:
const Hooks = require("../../Hooks/Hooks");
const userServices = require("./user.services");
module.exports = (app) => {
app
.use("/users", {
find: userServices.find,
create: userServices.createUser,
})
.hooks({
before: {
find: [Hooks.isValidDomain],
create: [Hooks.isValidDomain],
},
});
app
.use("/users/:code/validate", {
update: userServices.validateCode,
})
.hooks({
before: {
update: [Hooks.isValidDomain, Hooks.normalizedCode],
},
});
app
.use("/users/personal", {
update: userServices.personalInfo,
})
.hooks({
before: {
update: [Hooks.isValidDomain, Hooks.isValidToken],
},
});
};
Why Hooks.isValidToken applies to all my update methods? Even if I'm not calling it?
Please help.
app.hooks registers an application level hook which runs for all services. If you only want it for a specific service and method it needs to be app.service('users').hooks().

Localized routes in koa

I'm developing a site with multiple languages. Some routes will therefore also have to be localized and I'm not sure how to do this properly.
I'm using #koa/router for routing.
For this example it's only English and Swedish but the site will handle more languages.
I can setup routes to match words in different languages like
router.get('/(create-account|skapa-konto)/', (ctx, next) => {
ctx.body = translate('signup_welcome');
await next();
});
But, I want the English site to only respond to '/sign-up' and send 404 for '/skapa-konto' (and vice versa).
In the real world the route would point to some controller function. So if I set up individual routes for each language I would have to change all localized routes manually should the controller function change in the future. That's something I would like to avoid ;)
Any suggestions?
I ended up solving this by extending the Router like this:
const LocalizedRouter = class extends Router {
/**
* Set up route mapping
* #param {object} options
*/
constructor(options) {
if (!Array.isArray(options.languages)) {
throw new TypeError('Languages must be of type Array');
}
super(options);
this.languages = options.languages;
}
/**
* Router function for GET method
* #param {string | Object<string, string>} RouteCollection
*/
get(routes, func) {
if (typeof(routes) === 'string') {
super.get(routes, func);
return;
}
if (typeof(routes) === 'object') {
for(const key in routes) {
if(!this.languages.includes(key)) {
continue;
}
if(typeof(func) !== 'function') {
throw new TypeError('Middleware must be a function');
}
const checkLanguageAndMount = async (ctx, next) => {
if(ctx.state.lang !== key) {
return next();
}
return func(ctx, next);
};
super.get(routes[key], checkLanguageAndMount);
}
return;
}
throw new TypeError('"Routes" must be a string or an object');
}
};
I can then set up my routes like this:
const myRouter = new LocalizedRouter({
languages: ['en', 'sv']
});
myRouter.get({
'en': '/create-account',
'sv': '/skapa-konto'
}, (ctx, next) => {
ctx.body = translate('signup_welcome');
await next();
};
This can probably be cleaned up but it does solve what I wanted to do.
EDIT: Fixed bug that caused 404 if two languages had identical paths
This problem interested me so I created a small github repo with some code. I'll try to explain here:
I created an array with some options:
const localeConfig = [
{
locale: "en",
routes: [
{
path: "/sign-up",
controllers: [enController],
method: "GET",
},
],
prefix: false,
},
{
locale: "se",
routes: [
{
path: "/skapa-konto",
controllers: [seController],
method: "GET",
},
],
prefix: false,
},
];
I then pass this object to a setupRoutes function that basically iterates the array, generating all the routes according to those options.
const setupRoutes = (localeConfig) => {
// Have some check to prevent duplicate routes
localeConfig.forEach((opt) => {
// Adding prefix according to option
const localePrefix = opt.prefix ? `/${opt.locale}` : "";
opt.routes.forEach((route) => {
const path = `${localePrefix}${route.path}`;
router[route.method.toLowerCase()].apply(router, [
path,
...route.controllers,
]);
});
});
};
So, for instance, if you were to change any of the controllers in either language you would only need to update the specific locale object.route.controllers. I imagine you could even have each different locale in a different file to have some modularity.
The github repo is here and I would really like to have you contribute to it if you have any idea on how to improve this.
Cheers!

How to pass data to next middleware function in NodeJS

I'm writing my own implementation of middleware for a socket system. I've read through the source code of Laravel's middleware and some documentation on ExpressJS's implementation for inspiration.
However, I'm getting stuck on passing the data from one middleware handler to another.
I wrote a very basic example below. The output should be 4 but I don't know how to pass the output from one handler to the other. I'm guessing by setting a temporary variable, but I'm not sure how performant that is.
let each = require('lodash/each')
class Middleware {
constructor() {
this.handlers = [
function (data) {return data + 1},
function (data) {return data + 2}
]
}
}
class Router {
constructor() {
this.middleware = new Middleware
}
route(data) {
each(this.middleware.handlers, function(handler) {
handler(data) // no idea what to do here
})
}
}
class Socket {
constructor() {
this.router = new Router
}
write(data) {
return this.router.route(data)
}
}
let router = new Router
console.log(socket.write(1)) // should be 4
Change the route function inside Router class as following and the result of socket.write(1) will be 4:
class Router {
constructor() {
this.middleware = new Middleware
}
route(data) {
each(this.middleware.handlers, function (handler) {
data = handler(data)
})
return data
}
}

Breeze Error: A MergeStrategy of 'Disallowed'

I am getting this error when running the sample application for AngularJS from Breeze's website.
This is the code for the controller breezectl.js:
'use strict';
angular.module('mean').controller('breezeController', ['$scope', 'Global', 'dataservice',
function($scope, Global, dataservice) {
$scope.global = Global;
$scope.breeze = {
name: 'Breeze Sample'
};
//$scope.results = dataservice;
function getProducts() {
function success(data) {
$scope.results = data;
}
function failed(error) {
$scope.results = error.message;
}
dataservice.getAllProducts()
.then(success)
.catch(failed);
}
getProducts();
}
]);
dataservice.getAllProducts() enters the catch(failed) branch with this error message: "A MergeStrategy of 'Disallowed' does not allow you to attach an entity when an entity with the same key is already attached"
This is the code for dataservice.js:
'use strict';
angular.module('mean').factory('dataservice', ['breeze', 'entityManagerFactory',
function(breeze, entityManagerFactory) {
var manager = entityManagerFactory.newManager();
function getAllProducts(){
function success(data) {
return data.results;
}
return breeze.EntityQuery.from('Products')
.using(manager).execute()
.then(success);
}
var service = {
getAllProducts: getAllProducts
};
return service;
}
]);
Note: A direct call to Products from the Restful API (localhost:3000/breeze/northwind/Products) works properly and returns a set of Json objects representing all of the products in the collection.
Steve Schmitt are right. My metadata.json had the "defaultResourceName" property with a different name that the database collection.
I changed "Products" to "products" and this works.
Many thanks to all of you.

Resources