I'm trying to data in the Note collection from a local database of MongoDB using Mongoose with Nextjs and getting an error "Cannot read properties of undefined (reading 'note')"
Any ideas on what I am doing wrong?
import mongoose from 'mongoose';
const { Schema } = mongoose;
const NoteSchema = new Schema(
{
title: {
type: String,
maxlength: 60,
},
description: {
type: String,
maxlength: 200,
},
},
{ timestamps: true }
);
export default mongoose.models.note || mongoose.model('note', NoteSchema)
import dbConnect from "../../../lib/dbConnect"
import Note from "../../../models/Note"
export default async function handler(req, res) {
const { method } = req
await dbConnect()
if (method === 'GET') {
try {
const note = await Note.find({})
res.status(200).json({ success: true, data: note})
} catch (error) {
res.status(400).json({ success: false })
}
}
if (method === 'POST') {
try {
const note = await Note.create(req.body)
res.status(200).json({ success: true, data: note})
} catch (error) {
res.status(500).json(error)
}
}
}
import mongoose from 'mongoose'
const {MONGODB_URI} = process.env
let cached = global.mongoose
if (!cached) {
cached = global.mongoose = { conn: null, promise: null }
}
async function dbConnect() {
if (cached.conn) {
return cached.conn
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology:true,
bufferCommands: false,
}
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
return mongoose
})
}
cached.conn = await cached.promise
return cached.conn
}
export default dbConnect
I get the following
Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'note')
I was able to fix it by changing the export module
global.PizzaSchema = global.NoteSchema || mongoose.model('Note', NoteSchema);
export default global.NoteSchema;
Related
I am trying to pre save and hash password with bcrypt in mongoose in my next.js project, but password still unhashed. i tryed every link in stackoverflow and didnt solve it, the password still saved unHashed.
mongoose version: 6.9.1
this is my users.model file:
import {
models,
model,
Schema,
} from 'mongoose';
import bcrypt from 'bcrypt';
const UserSchema: Schema = new Schema({
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
displayName: {
type: String,
required: true,
},
role: {
type: String,
},
});
UserSchema.pre('save', function (next) {
console.log('Pre-Save Hash has fired.');
let user = this;
bcrypt.genSalt(10, (err, salt) => {
if (err) console.error(err);
bcrypt.hash(user.password, salt, (err, hash) => {
user.password = hash;
next();
});
});
});
const UserModel = models.Users || model('Users', UserSchema, 'users');
export default UserModel;
this is my adding function file:
import dbConnect from '#/utils/mongodb';
import UserModel from '#/models/user.model';
import { NextApiRequest, NextApiResponse } from 'next';
import { MongoError } from 'mongodb';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// const { email, password } = req.query;
try {
dbConnect();
const query = req.body;
const newUser = new UserModel(query);
const addedUser= await newUser.save(function (err: MongoError) {
if (err) {
throw err;
}
});
res.status(200).json(addedUser);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Internal server error' });
}
}
i cant see the 'Pre-Save Hash has fired.' in my console also..
// You need to add user.isModified("password")
userSchema.pre("save", function (next) {
var user = this;
if (user.isModified("password")) {
bcrypt.genSalt(SALT_I, (err, salt) => {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
next();
}
});
userSchema.methods.comparePassword = function (candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, (err, isMatch) => {
if (err) return cb(err);
cb(null, isMatch);
});
};
// to make register end point
import mongoose from "mongoose";
import User from "../../../models/User";
const dbConnect = async () => {
mongoose
.connect("mongodb://localhost:27017/test")
.then(() => {
console.log("Connected to mongoDb");
})
.catch((error) => {
console.log(error);
});
};
export default async function handler(req, res) {
try {
await dbConnect();
const query = req.body;
const newUser = new User(query);
await newUser.save(function (err, result) {
if (err) {
throw err;
} else {
res.status(200).json(result);
}
});
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal server error" });
}
}
thanks to all.
The problem was in my dbconnect file!
i need to secure the API so that only authorized user can access them. I followed the documentation in this link https://next-auth.js.org/tutorials/securing-pages-and-api-routes#securing-api-routes but apparently I am not retrieving the session.
I am able to console.log the authOptions but if I try to console log the session (and I am logged in), it logs "null"
This is the code
pages/api/profile.js
import prisma from "../../../lib/prisma";
import { unstable_getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req, res) {
const session = await unstable_getServerSession(req, res, authOptions);
console.log("SESSION", session); // this logs "null"
if (!session) {
return res.status(401).json("Not authorized");
}
try {
const user = await prisma.user.findUnique({
where: { email: session.user.email },
});
return res.status(200).json(user);
} catch (error) {
console.error(error);
return res
.status(503)
.json(
"Our server is not able to process the request at the moment, please try again later!"
);
}
pages/api/auth/[...nextauth].js
import NextAuth from "next-auth";
import CognitoProvider from "next-auth/providers/cognito";
import prisma from "../../../lib/prisma";
export const authOptions = {
providers: [
CognitoProvider({
clientId: process.env.CLIENTID_NEXTAUTH,
issuer: process.env.COGNITO_ISSUER,
clientSecret: process.env.CLIENTSECRET_NEXTAUTH,
}),
],
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60,
updateAge: 24 * 60 * 60,
},
callbacks: {
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token;
}
return token;
},
async session({ session, token }) {
const user = await prisma.user.findUnique({
where: { email: session?.user?.email },
});
if (!user) throw new Error("User not found in the database.");
const mySession = {
...session,
accessToken: token.accessToken,
email: user.email,
};
return mySession;
},
},
};
export default NextAuth(authOptions);
pages/dashboard/index.js
import axios from "axios";
import React, { useState } from "react";
import { getSession, useSession } from "next-auth/react";
const Dashboard = (props) => {
let { data: session, status } = useSession();
if (status === "loading") {
return <p>Loading...</p>;
}
if (status === "unauthenticated") {
window.location.reload();
}
return (
<p>
{props.userInfo.name}
</p>
);
};
export default Dashboard;
export async function getServerSideProps(context) {
const session = await getSession(context);
if (!session) {
return {
redirect: {
destination: "/",
permanent: false,
},
};
}
console.log("SESSION IN INDEX", session); // this logs the session
const userInfo = await axios.get(
`${process.env.BASE_URL}/api/profile?email=${session.email}`
);
return {
props: {
session,
userInfo: userInfo.data ? userInfo.data : null,
},
};
}
so when I login, I can see the SESSION in INDEX but when I hit the api/profile, the session from unstable_getServerSession is null, so I canno see nothing in the dashboard
resolved:
when calling the api you need to pass the headers, for example in the dashboard/index.js
const userInfo = await axios.get(
`${process.env.BASE_URL}/api/profiles/profile?email=${session.email}`,
{
withCredentials: true,
headers: {
Cookie: context.req.headers.cookie,
},
}
);
while in the API endpoint
import { getServerSession, getSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req, res) {
const session = await getServerSession(req, res, authOptions);
console.log("SESSION", session);
//your code
}
The following works fine, but I have noticed that it is really slow login in a client. How can I make it faster?
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import { ObjectId } from 'mongodb'
import { MongoDBAdapter } from "#next-auth/mongodb-adapter"
import clientPromise from "../../../lib/mongodb";
import { v4 as uuidv4 } from 'uuid';
var CryptoJS = require("crypto-js");
const sFinder = async (task, token) => {
try{
const client = await clientPromise;
const database = client.db('DRN1');
const ses = await database.collection('sessions');
switch (task) {
case 1:
const result = await ses.find({
"userId": ObjectId(token.uuid)
}).sort({"_id":-1}).limit(1).toArray();
if (!result) {
return 202;
}
else{
return result[0].sessionToken
}
break;
case 2:
const insertResult = await ses.insertOne({"userId":token.uuid, "sessionToken":token.accessToken});
if (!insertResult) {
return 203;
}
else{
return insertResult
}
break;
case 3:
var expdate = new Date(token.exp * 1000);
const UpdateResult = await ses.updateOne({"userId":ObjectId(token.uuid), "sessionToken":token.accessToken},
{ $set: {"expires": expdate}}, { upsert: true });
if (!UpdateResult) {
return 203;
}
else{
return UpdateResult
}
break;
default:
break;
}
} catch(e){
console.error(e);
}
}
export default NextAuth({
adapter: MongoDBAdapter(clientPromise),
session: {
strategy: 'jwt',
jwt: true,
},
providers: [
CredentialsProvider({
name: 'DRN1',
credentials: {
username: { label: "Username", type: "text"},
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
try{
const client = await clientPromise;
const database = client.db('DRN1');
const users = await database.collection('users');
const result = await users.findOne({
username: credentials.username,
});
if (!result) {
throw new Error('No user found with the username');
}
var bytes = CryptoJS.AES.decrypt(result.password, process.env.PASS_ENC);
var decryptedData = bytes.toString(CryptoJS.enc.Utf8);
//Check hased password with DB password
if(decryptedData != credentials.password){
throw new Error('Password doesnt match');
}
return {uuid:result._id, username: result.username, email: result.email, type:result.type, "sessionID":uuidv4()};
} catch(e){
console.error(e)
}
}
})
],
callbacks: {
signIn: async ({ user, account, profile, email, credentials }) => {
account.accessToken = user.sessionID
account.uuid = user.uuid
const test = await sFinder(2,account)
return true
},
jwt: async ({ token, account }) => {
if (account) {
token.uuid = account.uuid
token.accessToken = account.accessToken
}
const lastUsedToken = await sFinder(1,token)
const updateTokenExpire = await sFinder(3,token)
if(lastUsedToken != token.accessToken){
// console.log("I have made it an error")
token.error = 555;
}
return token
},
session: async ({ session, token, user }) => {
session.uuid = token.uuid
if(!token.accessToken){
//OAUTH Accounts
session.accessToken = uuidv4()
}else{
session.accessToken = token.accessToken
}
if(token.error == 555){
session.error = 555
}
return session
}
},
pages:{
error: 'signin'
},
theme: {
colorScheme: "dark", // "auto" | "dark" | "light"
brandColor: "", // Hex color code
logo: "https://storage.googleapis.com/radiomedia-images/station_logos/v2/DRN1_small.png" // Absolute URL to image
}
});
I believe what is slowing it down is the following
callbacks: {
signIn: async ({ user, account, profile, email, credentials }) => {
account.accessToken = user.sessionID
account.uuid = user.uuid
const test = await sFinder(2,account)
return true
},
jwt: async ({ token, account }) => {
if (account) {
token.uuid = account.uuid
token.accessToken = account.accessToken
}
const lastUsedToken = await sFinder(1,token)
const updateTokenExpire = await sFinder(3,token)
if(lastUsedToken != token.accessToken){
// console.log("I have made it an error")
token.error = 555;
}
return token
},
session: async ({ session, token, user }) => {
session.uuid = token.uuid
if(!token.accessToken){
//OAUTH Accounts
session.accessToken = uuidv4()
}else{
session.accessToken = token.accessToken
}
if(token.error == 555){
session.error = 555
}
return session
}
},
Mainly all the awaits, but the await functions are to make sure the user is not login on another device. As we log the old devices out automatically.
From the Fastify documentation in the section titled Using other validation libraries I'm trying to get yup to validate my schema but I keep getting schema.validateSync is not a function and I don't know why??
I want the schema to still be valid for creating the swagger document but I want to use yup to give me the validation I need.
const yup = require("yup");
const yupOptions = {
strict: false,
abortEarly: false,
stripUnknown: true,
recursive: true,
};
async function isUsernameAvailable(fastify: any, _options: Object) {
const users = fastify.mongo.db.collection("users");
fastify.get(
"/api/v1/onboarding/isUsernameAvailable/:username",
{
schema: {
params: {
type: "object",
properties: {
username: { type: "string", maxLength: 12, minLength: 1 },
},
required: ["username"],
},
response: {
200: {
type: "object",
properties: {
available: {
type: "boolean",
description: "Returns true if username is available",
},
},
},
},
},
validatorCompiler: ({ schema, method, url, httpPart }: any) => {
return function (data: any) {
try {
const result = schema.validateSync(data, yupOptions);
return { value: result };
} catch (e) {
return { error: e };
}
};
},
},
async (request: any, _reply: any) => {
const { username } = request.params;
const foundNUsernames = await users.countDocuments(
{ username },
{ limit: 1 }
);
const available: boolean = foundNUsernames === 0;
return { available };
}
);
}
export { isUsernameAvailable };
if I use the below, the validation works but the swagger doc doesn't build
schema: {
params: yup.object({
username: yup.string().lowercase().max(12).min(1).required(),
}),
}
if I remove the validatorCompiler then I get no errors, swagger does build but I cant use yup
validatorCompiler: ({ schema, method, url, httpPart }: any) => {
return function (data: any) {
try {
const result = schema.validateSync(data, yupOptions);
return { value: result };
} catch (e) {
return { error: e };
}
};
},
}
how can I satisfy both?
Why do I want to use yup? I want to validate emails and transform values into lowercase.
I connect GraphQL with MongoDB but result is null. Help me !
index.js file:
const { graphql } = require('graphql');
const { MongoClient } = require('mongodb');
const assert = require('assert');
const readline = require('readline');
const mySchema = require('./schema/main.js');
const rli = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const MONGO_URL = 'mongodb://127.0.0.1:27017/test';
MongoClient.connect(MONGO_URL, { useNewUrlParser: true }, (err, db) => {
assert.equal(null, err);
console.log('Connected to MongoDB server');
rli.question('Client Request: ', inputQuery => {
graphql(mySchema, inputQuery, {}, { db }).then(result => {
console.log('Server Answer: ', result.data);
db.close(() => rli.close());
});
});
});
main.js file:
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLInt
} = require('graphql');
const queryType = new GraphQLObjectType({
name: 'RootQuery',
fields: {
usersCount: {
type: GraphQLInt,
resolve: (_, args, { db }) => db.collection('users').count()
}
}
});
const mySchema = new GraphQLSchema({
query: queryType
});
module.exports = mySchema;
And this is result:
Connected to MongoDB server
Client Request: { usersCount }
Server Answer: { usersCount: null }
From what i could understand the db object you get from MongoClient connect callback is not your db object but instead the client object so
you should first call
const db = client.db
see Connect to MongoDB
this is something that you can find and fix easily if you can debug your code