Node.js - Joi schema: Check req.value & ip not working - node.js

I have this joi schema to validate email and password that was sent via body as json. In addition I now want to check the ip address. I tried it like below but I get this:
{
"isJoi": true,
"name": "ValidationError",
"details": [
{
"message": "\"ip\" is required",
"path": [
"ip"
],
"type": "any.required",
"context": {
"key": "ip",
"label": "ip"
}
}
],
"_object": {
"email": "loremlipsum02#abc.at",
"password": "kyuser2923?"
}
}
Here my joi schema in routehelpers.js
const Joi = require('joi');
module.exports = {
validateSignup: (schema) => {
return (req, res, next) => {
const result = Joi.validate(req.body, schema);
if (result.error) {
return res.status(400).json(result.error);
}
if (!req.value) { req.value = {}; }
req.value['body']= result.value;
next();
}
},
schemas: {
signupSchema: Joi.object().keys({
email: Joi.string().email().required(),
password: Joi.string().required(),
ip: Joi.string().ip({
version: [
'ipv4',
'ipv6'
],
cidr: 'required'
}).required()
})
}
}
When I insert in my controller:
const curIP = req.ip;
console.log('IP: '+curIP);
I do get the ip in the console:
Server running at http://127.0.0.1:4002/
IP: 127.0.0.1
But how in addition can I validate the ip from the server requesting a signup?
I suppose it is because it is checking everything in req.value.body . But how do I need to alter validateSignup: (schema) to also check the ip that is requesting signup?

Looks like you are trying to match ip property in body while it's a property of req object.
Try:
const result = Joi.validate(Object.assign(req.body, { ip: req.ip }), schema);
Although I think better approach would be using a library like proxy-addr. That is going to extract correct client IP even when your server runs behind a reverse proxy.

Related

update controller fails to works as intended

I'm trying to make a nodejs\ExpressJs backend for a small project. The backend is split into services, controllers and router files. While testing the updateProduct (to update the fields of a product that already exists in database) function on postman using a x-www-form-urlencoded body, I got the following error:
{
"err": {
"stringValue": "\"{ name: 'Hp', description: 'Pavillion', price: '20000' }\"",
"valueType": "Object",
"kind": "ObjectId",
"value": {
"name": "Hp",
"description": "Pavillion",
"price": "20000"
},
"path": "_id",
"reason": {},
"name": "CastError",
"message": "Cast to ObjectId failed for value \"{ name: 'Hp', description: 'Pavillion', price: '20000' }\" (type Object) at path \"_id\" for model \"Product\""
}
}
Screen of postman form :
This is the code I used in their respective files, I tried logging a message into the console to see whether they are called upon, and yes they were called as intended.
//service file
const updateProduct = async(product)=>{
return await Product.findByIdAndUpdate(product._id, product)
}
//controller file
const updateProduct = async(req, res)=>{
try{
const result = await productService.updateProduct(req.body)
res.status(200).json(result)
}
catch(error){
res.status(500).json({err:error})
}
}
//router file
router.put("/:id", productController.updateProduct)
I tried changing the code in the service layer to the following, it didn't work, but it not generate the same problem.
const updateProduct = async(product) => {
return await Product.findByIdAndUpdate(
product._id,
{
$set: {
name: product.name,
price: product.price,
description: product.description
}
},
{ new: true }
);
};
If relevant I'm using Mongoose and other methods such as adding or deleting work as intended. Thanks
Problem:
You have to get the _id of the desired product from the params not from the req.body which return null like:
this:
return await Product.findByIdAndUpdate(product._id, product);
would turn into:
return await Product.findByIdAndUpdate(undefined, {
"name": "Hp",
"description": "Pavillion",
"price": "20000"
})
and of course, the record won't get updated.
Solution:
your controller should look like this (I've used the path):
//controller file
const updateProduct = async(req, res)=>{
try{
const result = await productService.updateProduct(req.params.id, req.body)
res.status(200).json(result)
}
catch(error){
res.status(500).json({err:error})
}
}
and your service should look like this:
//service file
const updateProduct = async(id, product)=>{
return await Product.findByIdAndUpdate(id, product)
}
``

Implementation of an Express server with MongoDB MongoClient

I'm new with both of these technologies and really need help.
I have MongoDB set up on my computer and I have mongod running.
I have a database called 'memories-db' & a collection called 'entries', I can run mongosh in a different terminal and am able to get this output from
db.entries.find()
[
{
_id: ObjectId("62e81903809f6ae89c864a4d"),
id: 201901011245,
upvotes: 0,
comments: []
},
{
_id: ObjectId("62e81903809f6ae89c864a4e"),
id: 202001011246,
upvotes: 0,
comments: []
},
{
_id: ObjectId("62e81903809f6ae89c864a4f"),
id: 202201011247,
upvotes: 0,
comments: []
},
{
_id: ObjectId("62e81903809f6ae89c864a50"),
id: 201401011248,
upvotes: 0,
comments: []
}
]
I have an Express server defined as
// Boilerplate
// Import express & its types
import express, { Express, Request, Response } from 'express'
// MongoDB
// Import MongoClient & MongoDB types
import { MongoClient, Db, Collection, WithId, Document } from 'mongodb'
// Constant to store express object
const app: Express = express()
// Constant to store MongoDB URI
const mongoURI: string = 'mongodb://localhost:27017'
// Constant to store port number to listen on
const port: number = 8000
// GET Routes
app.get(
'/api/entries/:entryId',
(req: Request, res: Response) => {
const entryId: number = +req.params.entryId
MongoClient.connect(
mongoURI,
{},
async (error, client) => {
if (error || !client)
res.status(500).json({ message: 'Error connecting to db', error })
else {
const db: Db = client.db('memories-db')
const entryInfo: WithId<Document> | null = await db.collection('entries').findOne({ id: entryId })
res.status(200).json(entryInfo)
client.close()
}
}
)
}
)
app.listen(
port,
() => {
console.log(`⚡️[server]: Server is running at http://localhost:${port}`)
}
)
But when I make GET request I always get an error, please tell me what I'm doing wrong
{
"message": "Error connecting to db",
"error": {
"reason": {
"type": "Unknown",
"servers": {},
"stale": false,
"compatible": true,
"heartbeatFrequencyMS": 10000,
"localThresholdMS": 15
}
}
}
It turns out that my PC wasn't resolving the local MongoDB URI properly.
So, replacing
'mongodb://localhost:27017'
to
'mongodb://127.0.0.1:27017'
fixed the issue

Validation error of type UnknownType: Unknown type CreateUserInput

Using AppSync Query Playground running this query.
mutation UserMutation ($input: CreateUserInput!) {
createUser
(input: $input)
{
uniqueID
recordType
userName
userEmail
userEmailVerified
userCreated
userLastModified
userEnabled
userStatus
userValidFrom
userValidTo
}
}
Query Variables are
{
"input":
{
"uniqueID": "UUID-formatted",
"recordType": "USER",
"userName": "username",
"userEmail": "mailadres",
"userEmailVerified": true,
"userCreated": "2020-12-04T22:32:37.000Z",
"userLastModified": "2020-12-04T22:34:15.000Z",
"userEnabled": true,
"userStatus": "CONFIRMED",
"userValidFrom": "2021-03-12T11:03:37.283Z",
"userValidTo": "9999-12-31T23:59:59.999Z"
}
}
Creates a record in the DynamoDB table as expected.
Which suggest to me that the model and the resolvers are well defined in AppSync.
Running the exact same code in a NodeJS Express environment creates the above error.
API-key and GraphQL endpoint are correct. Exactly the same method for other entity used and works.
This is the NodeJS code
exports.userCreate = (cognitosub, userName, cognitoemail, cognitoemail_verified, cognitoUserCreateDate, cognitoUserLastModifiedDate, cognitoEnabled, cognitoUserStatus, userValidFrom, userValidTo) => {
const mutateQuery = gql(`
mutation UserMutation ($input: CreateUserInput!) {
createUser
(input: $input)
{
uniqueID
recordType
userName
userEmail
userEmailVerified
userCreated
userLastModified
userEnabled
userStatus
userValidFrom
userValidTo
}
}
`);
const mutateVariables = JSON.parse(`{
"uniqueID" : "${cognitosub}",
"recordType" : "USER",
"userName" : "${userName}",
"userEmail" : "${cognitoemail}",
"userEmailVerified" : ${cognitoemail_verified},
"userCreated" : "${cognitoUserCreateDate}",
"userLastModified" : "${cognitoUserLastModifiedDate}",
"userEnabled" : ${cognitoEnabled},
"userStatus" : "${cognitoUserStatus}",
"userValidFrom" : "${utils.dateConvertToISOString(userValidFrom)}",
"userValidTo" : "${utils.dateConvertToISOString(userValidTo)}"
}`)
return new Promise ((resolve, reject) => {
console.debug(`${Date(Date.now())} - utilities.userCreate.mutateVariables`,mutateVariables)
graphqlClient.mutate({
mutation: mutateQuery,
fetchPolicy: 'no-cache', // Mutate suporteert alleen 'no-cache'
variables: {input: mutateVariables}
})
.then((success) => {
// console.debug(`${Date(Date.now())} - utilities.userCreate.then`,success)
if (success === null) {reject('userIsNull')}
else {
resolve(success.data.createUser.uniqueID)}
})
.catch((err) => {
console.debug(`${Date(Date.now())} - utilities.userCreate.catch\n`,err)
reject(err)
})
})
Exact same code is used for a less complicated object with an UUID, Identification, validFrom en ValidTo date. It works like a charm.
I looked at every error and spelling mistake. The code keeps throwing this two errors.
graphQLErrors: [
{
path: null,
locations: [ { line: 1, column: 31, sourceName: null } ],
message: 'Validation error of type UnknownType: Unknown type CreateUserInput'
},
{
path: null,
locations: [ { line: 2, column: 3, sourceName: null } ],
message: "Validation error of type FieldUndefined: Field 'createUser' in type 'Mutation' is undefined # 'createUser'"
}
]
Which are dump of the error-object.
Appolo-client is used top access the DynamoDB. The records created in the AppSync GraphQL playground are perfectly viewable in the DB.
I am out of clues here. Can anyone help?
Today I really looked into this problem and decided not to use the AppSync client from AWS anymore (which created some depency build problems with every npm update by the way...)
I choose to go for the Apollo client latest version which doesn't give any npm update issues and is a up-to-date version of the client that AWS uses in the background if I am well informed (read can read fora in a good way ;-))
I had some issues with the authentication on AppSync but managed to get over this.
This code totally fixes ALL previous error (for me)
const gql = require("graphql-tag");
const ApolloClient = require("#apollo/client").ApolloClient
const ApolloLink = require("#apollo/client").ApolloLink
const InMemoryCache = require("#apollo/client").InMemoryCache;
const createHttpLink = require("#apollo/client").createHttpLink;
const {createAuthLink} = require("aws-appsync-auth-link")
require('cross-fetch/polyfill');
const clientGraphQL = new ApolloClient({
link: ApolloLink.from([
createAuthLink({
url: aws_secrets.AWS_DEV_APPSYNC_ENDPOINT,
region:aws_secrets.REGION,
auth:
{
type: "API_KEY",
apiKey: aws_secrets.AWS_DEV_APPSYNC_API_KEY,
}
}),
createHttpLink({
uri: aws_secrets.AWS_DEV_APPSYNC_ENDPOINT
}),
]),
cache: new InMemoryCache(),
});
Safe code by hiding all the secrets in a special file.
createAuthLink is the only thing I need from AWSAppSync (IMHO).
I didn't manage to get a proper connection with the CreateAuthLink from the Apollo Client.
Hope this helps some of you...

How to access post parameters from bodyParser using moleculer.js

I am digging into moleculer.js the only thing i am finding difficult to understand;how to get parameters inside actions of a service
below given is my code
const ApiGateway = require("moleculer-web");
module.exports = {
name: "api",
mixins: [ApiGateway],
settings: {
port: process.env.PORT || 3000,
bodyParsers: {
json: true,
urlencoded: { extended: true }
},
routes: [{
path: "/api",
whitelist: [
"**"
]
}],
assets: {
folder: "public"
}
},
};
Below is my user service where i want to get post parameters
module.exports = {
name: "users",
dependencies: ["guard"],
actions: {
create: {
restricted: [
"api"
],
async handler(ctx,route, req, res) {
this.logger.info(req);
this.logger.info("'users.create' has been called.");
const token=await ctx.call("guard.generate",{service:"abc"});
what i want is
const token=await ctx.call("guard.generate",{service:req.body.name});
instead of
const token=await ctx.call("guard.generate",{service:"abc"});
const verify=await ctx.call("guard.check",{token:token});
return [token,verify,req];
}
},
}
Moleculer´s Actions has the following signature: <actionName> (ctx) {// logic} or <actionName>: { handler (ctx) { // logic}}.
So what you have to do is this:
module.exports = {
name: "users",
actions: {
welcome: {
handler(ctx) {
console.log(ctx.params) // Print the request params
// Call other actions ctx.call('serviceName.actionName`, ...data...)
return ctx.params
}
}
}
}
More info about Actions: https://moleculer.services/docs/0.13/actions.html
The function signaturehandler(ctx,route, req, res) is a route hook that is used only in API gateway.
More info about Route hooks: https://moleculer.services/docs/0.13/moleculer-web.html#Route-hooks
Also, the req and res can't be passed to other services because these objects are not serializable.
Anyway, you might consider checking the video tutorial: https://www.youtube.com/watch?v=t4YR6MWrugw
It covers Moleculer's core concepts and shows how to call actions

Deleting a single item from a database with mongodb

{
"_id": {
"$oid": "5a2de0a00d6baa43e8b925d0"
},
"name": "test",
"playList": [
{
"url": "https://p.scdn.co/mp3-preview/8aa799e60164f8a1fb311188d9d85ef65d7782c6?cid=ed36a056ee504173a3889b2e55cbd461",
"artist": "Kenny G",
"songName": "My Heart Will Go On (Love Theme from \"Titanic\")",
"_id": {
"$oid": "5a2de0ad0d6baa43e8b925d1"
}
},
{
"url": "https://p.scdn.co/mp3-preview/7c49854f18e6dfda6cd97ab5e8bc139d7ca82b7c?cid=ed36a056ee504173a3889b2e55cbd461",
"artist": "PRODUCE 101",
"songName": "PICK ME",
"_id": {
"$oid": "5a2de13b0d6baa43e8b925d2"
}
}
],
"__v": 0
}
I have a database called channels where each channels contain a playList as shown below. I want to delete a single item when a button is clicked. I can handle the onClick event part, but I am not sure how to implement the routes part.
I know that I start by doing something like
router.delete(''/channels/:id', function(req, res){
something here...
})
but how can I access a particular item (probably with a unique id?) and delete it from the DB?
EDIT
By using the GET below
router.get('/channels/:id',
isLoggedIn,
function(req, res) {
channel.findOne({'name':req.params.id},function(err,channeldata){
if(err || channeldata === null){
res.status(404).send({
message: 'Channel Not Found',
data: []
})
}
else {
res.status(200).json({
message: "channel to "+req.params.id+"success",
data:channeldata
})
}
})
});
I get the data for a single channel in my DB.
But since I am new to this stuff, I am not sure how to access each item of the playList and delete a single data.
EDIT2
var mongoose = require('mongoose');
var ChannelSchema = new mongoose.Schema({
name: {type:String,required:true},
playList: [{
songName: { type : String },
artist: { type : String },
url: { type : String }
}]
})
module.exports = mongoose.model('Channel',ChannelSchema);
You can try the following snippet that contains the DELETE (part of CRUD) endpoint for your resource collection (i.e. the channels):
router.delete('/channels/playlist/song', isLoggedIn, (req, res) => {
const channel_id = req.query.channelId;
const song_id = req.query.songId;
// the following query deletes a song form a playlist of a certain channel
channel.update({_id: ObjectId(channel_id)},{$pull:{playList:{_id:ObjectId(song_id)}}})
.exec()
.then(result => {
// for checking if document was found and deleted
// mongodb actually returns special object `result`
// which has its own certain fields
res.status(200).send({
status: "success",
message: result
});
})
.catch(error => {
// here we see if we had any problem with server or db itself
console.log(error)
res.status(500).send({
success: false,
message: "Something went wrong with DELETE /channels/:id"
})
})
});
I assume that you know what ObjectId() function does
if you do not have it declared, declare the following comment
in the beginning of the file (where you require everything)
const mongoose = require('mongoose'); // you must have this
const ObjectId = mongoose.Types.ObjectId; // gets the function
Let me know if this helps, or if you do not understand something - I will make an edit so that you get it.

Resources