How to find email in MongoDB using NextJS and mongoose - mongodb

I am trying to create a login endpoint that checks to see if an email is already stored in the database. If an email exists it will return an error, otherwise it notifies that an email exists. For some reason, User.findOne({ email: req.body.email }) does not seem to work. Here is the code I am currently using (located in pages/api/login.ts.)
import dbConnect from "../../lib/dbConnect";
import User from "../../models/User"
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
await dbConnect()
//type of request
const {method} = req
if (method === "POST") {
try {
await User.findOne({email: req.body.email}, function(err, user) {
if (err) {
res.status(400).json({error: "no email found"})
}
if (user) {
res.status(200).json({success: "email found", data: user})
}
})
} catch (error) {
res.status(400).json({error: "connection error"})
}
}
}

I never seen callback with await syntax:
try {
const user = await User.findOne({ email: req.body.email });
if (user) {
res.status(200).json({success: "email found", data: user})
}
} catch (error) {
// handle error here
}

Related

How to get current user ID with Next JS and next-auth within an API route?

I am trying to get the current user ID to push it in the creation of a document with mongodb.
I have created a specific APi route which get the data from a form.
However, I cannot use useSession to get session.user.id in an API route as I can in a basic react component. so how should I proceed to retrieve the current user ID?
This is the current code of the api/companies/create.js:
import { MongoClient } from "mongodb";
// import clientPromise from "../../../lib/mongodb";
async function handler(req, res) {
if (req.method === "POST") {
const { name, bio, size, location, logo, website, industry } = req.body;
// | (bio.trim() === "")
// BACKEND VALIDATION
if (!name || name.trim() === "") {
res.status(422).json({ message: "Invalid input." });
return;
}
// Storing it in the database
const newCompany = {
name,
size,
bio,
location,
logo,
website,
industry,
};
let client;
try {
// const client = await clientPromise;
client = await MongoClient.connect(process.env.MONGODB_URI);
} catch (error) {
res.status(500).json({ message: "Could not connect to database." });
return;
}
const db = client.db("main");
try {
const result = await db.collection("companies").insertOne(newCompany);
// Not sure about that line:
// newCompany.id = result.insertedId;
} catch (error) {
client.close();
res.status(500).json({ message: "Storing message failed!" });
return;
}
client.close();
res.status(201).json({ message: "Sucessfuly stored company" });
}
}
export default handler;
This is from: https://next-auth.js.org/configuration/nextjs
This is how I get a session on the server side in API routes
import type { NextApiRequest, NextApiResponse } from 'next'
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await unstable_getServerSession(req, res, authOptions)
// you can now use session.data
I think NextAuth ultimately wants you to use middleware to control certain API routes and pass session related data to API functionality.

How to solve client fetch error for next-auth authentication

I have created an app that connects to a mongodb cluster and stores user info. The user is then able to log in with Next-Auth functionality. The app was working just fine before deploying to Vercel. On the live site I ran into some Server Config Errors. I refractored my code yet I am still running into a few errors.
I am successfully able to connect to the database for a new user sign up.
import {
connectToDatabase,
hashedPassword,
} from "../../helper/HelperFunctions";
const isEmpty = (value) => value.trim() === "";
const isTenChars = (value) => value.trim().length >= 10;
const emailValidation = (value) => {
const pattern = /^[^ ]+#[^ ]+\.[a-z]{2,3}$/;
if (value.match(pattern)) {
return true;
} else {
return false;
}
};
export default async function handler(req, res) {
if (req.method == "POST") {
let data = req.body;
const { firstName, lastName, email, password, userName } = data;
const firstNameIsValid = !isEmpty(firstName);
const lastNameisValid = !isEmpty(lastName);
const emailIsValid = emailValidation(email);
const passwordisValid = isTenChars(password);
const userNameIsValid = !isEmpty(userName);
let userDataIsValid =
firstNameIsValid &&
lastNameisValid &&
emailIsValid &&
passwordisValid &&
userNameIsValid;
if (!userDataIsValid) {
return;
}
const client = await connectToDatabase();
const db = client.db();
const existingUser = await db.collection("users").findOne({ email: email });
if (existingUser) {
res.status(422).json({ message: "User already exists, please log in!" });
console.log("User already exists, please log in!");
client.close();
return;
}
const protectedPassword = await hashedPassword(password);
await db.collection("users").insertOne({
firstName: firstName,
lastName: lastName,
email: email,
password: protectedPassword,
userName: userName,
});
client.close();
res.status(201).json({ message: "Signed up!" });
} else {
res.status(200).json({ data: req.body });
}
}
Here is my nextauth api route
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
// Helper Functions
import {
connectToDatabase,
comparePasswords,
} from "../../../helper/HelperFunctions";
export default NextAuth({
session: { strategy: "jwt" },
providers: [
CredentialsProvider({
async authorize(credentials) {
const client = await connectToDatabase();
const userCollection = client.db().collection("users");
const user = await userCollection.findOne({
email: credentials.email,
});
if (!user) {
client.close();
throw new Error("No user found!");
}
const isValid = await comparePasswords(
credentials.password,
user.password
);
if (!isValid) {
client.close();
throw new Error("Invalid password");
}
client.close();
if (user) {
return {
email: user.email,
};
} else {
return null;
}
},
}),
],
});
Before I deployed my site on Vercel, this was working just fine on localhost. The user should then proceed to a new page if the result of logging in has no errors.
const result = await signIn("credentials", {
redirect: false,
email: form.email,
password: form.password,
});
if (!result.error) {
console.log(true);
router.replace("/suggestions");
} else {
console.log(result.error);
setLoginResult(result.error);
}
If you see CLIENT_FETCH_ERROR make sure you have configured the NEXTAUTH_URL environment variable.
when developing you set it to localhost:3000, now you need to set that to your deployed url.

How to access mongodb via the plugin fastify-mongodb

My plugin looks like
import fp from 'fastify-plugin';
import mongodb from 'fastify-mongodb';
export default fp(async (fastify) => {
fastify.register(mongodb, {
url: 'mongodb+srv://dbuser:password#cluster0.otigz.mongodb.net/myapp?retryWrites=true&w=majority',
});
});
and my handler looks like
const postJoinHandler = async (
request: any,
reply: any
): Promise<{ id: string; name: string }> => {
try {
const { username, password } = request.body;
const test = await reply.mongo.db.users.insertOne({
username,
password,
});
console.log(test);
return reply.code(201).send(username);
} catch (error) {
request.log.error(error);
return reply.send(400);
}
};
Expected it to insert the username and password into the collection named users, but it didn't? and the error is Cannot read property 'db' of undefined
I also tried
reply.mongodb.users.insertOne({...
and
const test = await request.mongodb.collection('users');
test.insertOne({
username,
password,
});
console.log(test);
and
const test = await this.mongo.db.collection('users'); //<= Object is possibly 'undefined'
Routes look like
import { FastifyPluginAsync } from 'fastify';
import { postJoinSchema, postLoginSchema } from '../schemas/auth';
const auth: FastifyPluginAsync = async (fastify): Promise<void> => {
fastify.post('/auth/join', postJoinSchema);
fastify.post('/auth/login', postLoginSchema);
};
export default auth;
The mongo decorator is attached to the fastify instance, not to the request nor reply object.
You should move your handlers into the routes file and read for fastify.mongo or use a named function as the handler.
In the latter case, the handler has this bounded to the fastify instance.
async function postJoinHandler (
request,
reply
) {
try {
const { username, password } = request.body;
const test = await this.mongo.db.users.insertOne({
username,
password,
});
console.log(test);
reply.code(201)
return username
} catch (error) {
request.log.error(error);
reply.code(400);
return {}
}
};

asyncData get profile from db

So i want to fetch from db using asyncdata and axios, Here's the code, The problem is that no request is sent, And i'm wondering if someone can help me catch the error.
async asyncData({ $axios, store }) {
try {
let profile = await $axios.$get('/profile', store.state.auth.id)
return { profile }
} catch (error) {
console.log(error.message)
}
},
router.get('/profile', async (req, res) => {
const { userId } = req.body
try {
const profileUser = await User.findById(userId)
res.send(profileUser)
} catch (e) {
console.log(e)
res.status(400).json(e.message)
}
})
You may need to modify your route to the following, if you want to pass the id as parameter
router.get('/profile/:id', async (req, res) => {
const { userId } = req.params.id;
try {
const profileUser = await User.findById(userId)
res.send(profileUser)
} catch (e) {
console.log(e)
res.status(400).json(e.message)
}
})
and add profile id as route parameter
async asyncData({ $axios, store }) {
try {
let profile = await $axios.get('/profile/{profile_id_here}')
return { profile }
} catch (error) {
console.log(error.message)
}
}
Otherwise, if you want to get the id of the authenticated user (may be resolved from a Bearer token), it needs to be set to the request object in you authentication middleware.
In your authentication middleware,
const user = await _authService.validateFromToken(bearerToken);
if (user) {
req.user = user;
}
then you can access authenticated user as,
router.get('/profile', async (req, res) => {
const { userId } = req.user._id;
try {
const profileUser = await User.findById(userId)
res.send(profileUser)
} catch (e) {
console.log(e)
res.status(400).json(e.message)
}
})

sailsjs services: done not a function

I have build a service function in sailsjs, which does basic authentication.
Once local.authenticate finishes, on err I am able to refer to error().
If there are no error, the sails js is unable to access done() and crashes with indication "done is not a function".
module.exports = {
auth: function (login, password, error, done) {
var local_auth = function (login, password) {
local.authenticate(login, password, function (err, user) {
if (err) {
return error({ err: err });
} else {
return done({ user: user });
}
});
};
local_auth(login, password);
}
};
I call the service from controller:
authService.auth(user, pass, function (err, user) { ... });
Friendly guys at sailsjs chat, showed me the error:
module.exports = {
auth: function (login, password, done) {
var local_auth = function (login, password) {
local.authenticate(login, password, function (err, user) {
if (err) {
return done(err, null);
} else {
return done(null, user);
}
});
};
local_auth(login, password);
}
}