How to use Mongoose Populate between 2 different Schema's - express

I have 2 schema's, Categories and Cards. Each Category has an array of cards, and I want to populate that array with values , but I am unsure how to go about this as the mongoose documentation is somewhat confusing to understand.
// Schemas in seperate files
// Category Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categorySchema = new Schema({
title: {
type: String,
trim: true,
max: 30,
},
cards: [{ type: Schema.Types.ObjectId, ref: "categoryCard" }],
});
module.exports = mongoose.model("category", categorySchema);
// Category Card Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categoryCardSchema = new Schema({
category: {
type: String,
trim: true,
},
name: {
type: String,
trim: true,
},
post: {
type: String,
required: true,
trim: true,
},
});
module.exports = mongoose.model("categoryCard", categoryCardSchema);
// Below is the express router file . I want users to be able to create cards for different categories , after the category is already created. It worked in postman, but it doesn't work on the front end for some reason.
router.route("/createCard").post((req, res) => {
const { title, name, post } = req.body;
newCard = new categoryCard({
category: title,
name,
post,
});
newCard.save();
category.findOne({ title }).exec((err, item) => {
if (!err) {
item.cards.push(newCard._id);
item.save();
res.send(item);
} else {
res.send(err);
}
});
});

You can Follow this code...
let categorys= await category.findOne({ title }).populate("cards")

Related

how to handle image upload in the mern stack using multer?

Im creating a MERN stack application and in the react front end, i intend to have a form to add a product, the form is going to have a lot of inputs including an image upload option. I want to know how to handle the image upload from the express side using Multer. i have used their documentation but im not sure whether the code i wrote is correct. I also haven't created the front end yet, so i am currently using postman to test the api. How do i test whether the image upload functionality is working using postman? I would be posting the code i have written so far for context.
Product model:
const mongoose = require('mongoose')
const ProductSchema = new mongoose.Schema({
name:{
type: String,
required: [true, 'please provide a product name'],
maxlength: 20,
minlength: 3
},
category: {
type: String,
required: [true, 'please provide a category'],
maxlength: 20,
minlength: 3
},
quantity: {
type: Number,
required: [true, 'please provide the quantity']
},
price: {
type: Number,
required: [true, 'please provide the price']
},
description: {
type: String,
required: [true, 'please provide the description'],
trim: true
},
image: {
type: String
},
createdBy: {
type: mongoose.Types.ObjectId,
ref: 'User',
required: [true, 'Please provide the user'],
}, },
{ timestamps: true } )
module.exports = mongoose.model('Product', ProductSchema)
file upload.js:
const multer = require('multer')
const { v4: uuidv4 } = require('uuid')
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, '../uploads')
},
filename: function(req, file, cb){
cb(null, uuidv4() + '-' + Date.now() + path.extname(file.originalname) )
}
})
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png']
if(allowedTypes.includes(file.mimetype)) {
cb(null, true)
}else(
cb(null, false)
)
}
const upload = multer({storage, fileFilter})
module.exports = upload
Product router:
const express = require('express')
const router = express.Router()
const upload = require('../utils/fileUpload')
const {getAllProducts, createProduct, getProduct, updateProduct, deleteProduct} = require('../controllers/products')
router.route('/').post(upload.single('image'), createProduct).get(getAllProducts)
router.route('/:id').get(getProduct).patch(updateProduct).delete(deleteProduct)
module.exports = router
Product controller:
const Product = require('../models/Product')
const { StatusCodes } = require('http-status-codes')
const { BadRequestError, NotFoundError } = require('../errors')
const createProduct = async (req, res) => {
req.body.createdBy = req.user.userId
const product = await Product.create({...req.body, image: req.file})
res.send('create Product')
}
const getAllProducts = async (req, res) => {
res.send('get All products')
}
const getProduct = async (req, res) => {
res.send('get product')
}
const updateProduct = async (req, res) => {
res.send('update product')
}
const deleteProduct = async (req, res) => {
res.send('delete product')
}
module.exports = {
getAllProducts, createProduct, getProduct, updateProduct, deleteProduct
}
You can change the parameter type to file from postman to try uploading files when sending a request :

How can I Connect a Postgres Schema in Nodejs and Switching Schemas Dynamically Using Sequelize?

I have to connect to a Postgres database in Node.js and I want to switch schemas dynamically Using Sequelize Library.
Here is my Table Model and Controller Code.
module.exports = function (sequelize, DataTypes) {
const customer = sequelize.define(
'customer',
{
id: {
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
schema_name: {
type: DataTypes.STRING,
allowNull: false,
},
created_on: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW(),
},
},
{
schema: 'public',
}
)
return customer
}
And Controller Code
exports.login = async (req, res, next) => {
const { email, password, domain } = req.body
const domainData = await customer.findOne({
where: { schema_name: domain },
})
console.log('Log: exports.login -> domainData', domainData)
}
Once the response received I have to Switch Schemas Dynamically according to the above result.
Help me Guys Please
I resolved the problem using sequelize.query() function.
for getting Domain...
const domainData = await sequelize.query(
`SELECT schema_name from public.customer_management_client where schema_name = '${domain}'`
)
for switching to dynamic schema
const UserData = await sequelize.query(
`SELECT * from ${domainData[0][0].schema_name}.user_access_user where email = '${email}'`
)

Should a new Collection be created upon Model.create()

Am working with mongoose and have two models. The User model and the Service model, when a user logs in the method will findOne() user if one exists or create() a new user based on the what's passed in from req.body.
My Service Schema is like this:
const serviceSchema = new mongoose.Schema({
name: {
type: String,
default: 'contentEditor'
},
display: {
type: String,
default: 'Content Editor'
},
accessLevel: {
type: Number,
min: 0,
max: 4,
default: 4
}
});
My User Schema is a bit bigger, I've removed some of the field/value pairs but the part where I embed the Service Schema looks like this:
const userSchema = new mongoose.Schema(
{
email: {
type: String,
required: [true, 'Must have a email address'],
trim: true,
unique: true,
},
firstName: {
type: String,
},
lastName: {
type: String,
},
services: {
type: [serviceSchema],
ref: 'Services',
default: [serviceSchema],
},
},
);
When I hit the /api/v1/login endpoint a new user will be created with the Service document correctly but within the Mongoose database only a User collection exists. How do I make it so that both a Users collection and Services collection are created?
Edit: Below is the function that I create/find the user with when they login. When an existing User is found, by their email it will return that user if the user is not found then it will create a new one...
Both behaviours are as expected including adding the Services to the newly created User. What isn't expected is that only ONE collection is added to the DB.
const login = catchAsync(async ({ body: { email, password } }, res, next) => {
if (!email || !password) {
return next(new AppError('Please provide email and password', 400));
}
const { Success } = await webApi(email, password);
const mongoUser = await User.findOne({ email });
if (Success && mongoUser) {
return createSendtoken(mongoUser, 200, res);
}
if (Success && !mongoUser) {
const newUser = await User.create({ email });
return createSendtoken(newUser, 201, res);
}
return next(new AppError('User not found', 404));
});
Make sure you are making the serviceSchema a mongoose model.
const Services = mongoose.model('Service', serviceSchema)
You also have to save it using mongooses model.save() function

How to link documents on mongoose

I am new to express development and i am trying to build a blog. I have built two models, one for posts and one for uses. On users schema i have an attribute posts to save the post when a user creates a post. On the controller, before i create a post first i am taking the user's id from the req.params and after that i retrive the user by findbyid function and try to save the post on user's posts attribute, but with no succeful.
const mongoose = require("mongoose");
UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
posts: [{type: mongoose.Schema.Types.ObjectId, ref: "Post"}]
})
module.exports = mongoose.model("User", UserSchema);
const Post = require("../model/post");
const User = require("../model/user");
module.exports = {
new: (req, res) => {
res.render("new_post");
},
post_new: (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = new Post({
title: title,
article: article,
})
User.findById(id)
.then(user => {
user.posts.push(post);
})
//post.created_by.push(id);
post.save()
.then(result => {
console.log("Post has created");
res.redirect("/");
});
}
};
I see a few problems.
You user schema should not have array of posts. Instead, your post schema should have a field named user/userId to store user ID.
Example:
const PostSchema = new mongoose.Schema({
title: { type: String },
....,
userId: {type: mongoose.Schema.Types.ObjectId, ref: "User"}
});
Now your post_new function should be like this.
post_new: async (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = await Post.create({
title: title,
article: article,
userId: id
});
console.log("Post has created");
res.redirect("/");
}
If you want to stick with your way then the create_new function should be like this.
post_new: async (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = new Post({
title: title,
article: article,
});
const {_id} = await post.save();
const user = await User.findById(id);
user.posts.push(_id);
await user.save();
console.log("Post has created");
res.redirect("/");
}

How do I create custom directives to protect my GraphQL API?

I want to use custom directives to protect my GraphQL API. More specifically, for specific GraphQL fields, I want to check if users have authorisation to query those fields when a request hits my GraphQL server.
The following links are articles that contain examples on achieving this objective.
Link 1: https://www.prisma.io/blog/graphql-directive-permissions-authorization-made-easy-54c076b5368e/
Link 2: https://codeburst.io/use-custom-directives-to-protect-your-graphql-apis-a78cbbe17355
However, both examples achieve this by first constructing their Schema using the GraphQL Schema Definition Language (below is a snippet from the repo for Link 2) that demonstrates how you can use custom directives to check if users have authorisation to query specific fields (such as "rating").
require('dotenv').config();
const express = require('express');
const graphqlHTTP = require('express-graphql');
const {
makeExecutableSchema
} = require('graphql-tools');
const {directiveResolvers} = require('./directives');
const {allProductsBySupplier, addProduct, product, suppliers} = require('./resolvers');
require('./auth');
const app = express();
const port = 8080;
const typeDefs = `
directive #isAuthenticated on QUERY | FIELD
directive #hasScope(scope: [String]) on QUERY | FIELD
type Product {
id: ID!
supplierId: ID!
sku: String
qty: Int
price: Int
parrot: String
rating: Int #hasScope(scope: ["read:rating"])
}
type Supplier {
id: ID!
name: String!
}
input ProductInput {
supplierId: ID!
sku: String!
qty: Int!
price: Int!
parrot: String!
rating: Int!
}
type Query {
allProductsBySupplier: [Product] #isAuthenticated
product: Product #isAuthenticated
suppliers: [Supplier]
}
type Mutation {
addProduct(input: ProductInput!): Product #hasScope(scope: ["add:product"])
}
`;
const resolvers = {
Query: {
allProductsBySupplier,
product,
suppliers
},
Mutation: {
addProduct
}
};
const schema = makeExecutableSchema({
typeDefs,
resolvers,
directiveResolvers
});
app.use(
'/graphql',
graphqlHTTP({
schema,
graphiql: true
})
);
app.listen(port);
console.log(`server running on localhost:${port}`);
I have constructed my API without using the GraphQL schema definition language as shown below. The following snippet has been extracted from the official graphql docs.
var express = require('express');
var graphqlHTTP = require('express-graphql');
var graphql = require('graphql');
// Maps id to User object
var fakeDatabase = {
'a': {
id: 'a',
name: 'alice',
},
'b': {
id: 'b',
name: 'bob',
},
};
// Define the User type
var userType = new graphql.GraphQLObjectType({
name: 'User',
fields: {
id: { type: graphql.GraphQLString },
name: { type: graphql.GraphQLString },
}
});
// Define the Query type
var queryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
// `args` describes the arguments that the `user` query accepts
args: {
id: { type: graphql.GraphQLString }
},
resolve: function (_, {id}) {
return fakeDatabase[id];
}
}
}
});
var schema = new graphql.GraphQLSchema({query: queryType});
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
How can I create custom directives to check if users have authorisation to query specific fields if I have constructed my Schema without using the GraphQL Schema Definition Language?