I have seperated my code into a controler and a database service and added in async/await to make sure I return the json response from mongo.db. Unfortunatley it keeps sending an empty object {}. Why is my await not doing anything, its just sending back an empty object. The same code applied to a simple find on an array of data returns the full mock object.
Controller
const postVacation = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const vacation: IVacation = {
id: undefined,
name: req.body.name,
description: req.body.description,
};
try {
const newVacation = await vacationData.addVacation(vacation);
res.status(201).json(newVacation);
} catch (error) {
next(new HttpException(500, error.toString()));
}
};
Service
const addVacation = async (vacation: IVacation): Promise<IVacationMongo> => {
const createdVacation = new Vacation({
name: vacation.name,
description: vacation.description,
});
return createdVacation.save();
};
Models
import mongoose from "mongoose";
const Schema = mongoose.Schema;
export interface IVacation {
id: string;
name: string;
description: string;
}
export interface IVacationMongo extends mongoose.Document {
name: string;
description: string;
}
const vacationSchema = new Schema({
name: { type: String, required: true },
description: { type: String, required: true },
});
const Vacation = mongoose.model<IVacationMongo>("Vacation", vacationSchema);
export default Vacation;
I was just missing an await in the service
return await createdVacation.save();
Related
Looking for a backend dev that can simply help me implement MONGODB with nextJS and the current model I have now. I have bought https://www.devias.io admin dashboard, and just want to implement auth and database reading with it.
Just want the basic auth setup. It's already setup in the FILES just wanting to know how to configure it properly based on the devias guides
Has anyone done this before I can't find any documentation on it
It's setup with mock data at the moment
SRC/API/AUTH/index.js
import { createResourceId } from '../../utils/create-resource-id';
import { decode, JWT_EXPIRES_IN, JWT_SECRET, sign } from '../../utils/jwt';
import { wait } from '../../utils/wait';
import { users } from './data';
class AuthApi {
async signIn(request) {
const { email, password } = request;
await wait(500);
return new Promise((resolve, reject) => {
try {
// Find the user
const user = users.find((user) => user.email === email);
if (!user || (user.password !== password)) {
reject(new Error('Please check your email and password'));
return;
}
// Create the access token
const accessToken = sign({ userId: user.id }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
resolve({ accessToken });
} catch (err) {
console.error('[Auth Api]: ', err);
reject(new Error('Internal server error'));
}
});
}
async signUp(request) {
const { email, name, password } = request;
await wait(1000);
return new Promise((resolve, reject) => {
try {
// Check if a user already exists
let user = users.find((user) => user.email === email);
if (user) {
reject(new Error('User already exists'));
return;
}
user = {
id: createResourceId(),
avatar: undefined,
email,
name,
password,
plan: 'Standard'
};
users.push(user);
const accessToken = sign({ userId: user.id }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
resolve({ accessToken });
} catch (err) {
console.error('[Auth Api]: ', err);
reject(new Error('Internal server error'));
}
});
}
me(request) {
const { accessToken } = request;
return new Promise((resolve, reject) => {
try {
// Decode access token
const { userId } = decode(accessToken);
// Find the user
const user = users.find((user) => user.id === userId);
if (!user) {
reject(new Error('Invalid authorization token'));
return;
}
resolve({
id: user.id,
avatar: user.avatar,
email: user.email,
name: user.name,
plan: user.plan
});
} catch (err) {
console.error('[Auth Api]: ', err);
reject(new Error('Internal server error'));
}
});
}
}
export const authApi = new AuthApi();
then /SRC/API/AUTH/data.js
export const users = [
{
id: '5e86809283e28b96d2d38537',
avatar: '/assets/avatars/avatar-anika-visser.png',
email: 'demo#devias.io',
name: 'Anika Visser',
password: 'Password123!',
plan: 'Premium'
}
];
This is the documentation on it
JSON Web Token (JWT)
Most auth providers use this strategy under the hood to provide access tokens. Currently, the app doesn't cover the backend service, and this service is mocked (faked) using http client interceptors. The implementation is basic, but enough to give you a starting point.
How it was implemented
Since tokens are meant to be created on the backend server, they are built with encrypt, encode and decode utility methods because they are not meant to be used on the client. These utilities can be found in src/utils/jwt. These are for development purposes only, and you must remove (or avoid using) them.
How to use JWT Provider
The app is delivered with JWT Provider as default auth strategy. If you changed or removed it, and you want it back, simply follow these steps:
Step 1: Import the provider
Open src/pages/_app.js file, import the provider and wrap the App component with it.
// src/pages/_app.js
import { AuthConsumer, AuthProvider } from '../contexts/auth/jwt-context';
const App = (props) => {
const { Component, pageProps } = props;
return (
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
);
};
Step 2: Set the hook context
Open src/hooks/use-auth.js file and replace the current context the following line:
import { AuthContext } from '../contexts/auth/jwt-context';
How to use auth
Retrieve user profile
In the example below, you can find how it can be used in any component not just the App. Should you want to use it in any other component, you'll have to import the useAuth hook and use it as needed.
// src/pages/index.js
import { useAuth } from '../hooks/use-auth';
const Page = () => {
const { user } = useAuth();
return (
<div>
Email: {user.email}
</div>
);
};
Auth methods / actions
For simplicity and space limitations, the code below is used only to exemplify, actual code can be found in the components.
// src/pages/index.js
import { useAuth } from '../hooks/use-auth';
const Page = () => {
const { login } = useAuth();
const handleLogin = () => {
// Email/username and password
login('demo#devias.io', 'Password123!');
};
s
return (
<div>
<button onClick={handleLogin}>
Login
</button>
</div>
);
};
Implemented flows
Currently, the app only covers the main flows:
Register
Login
Logout
const mongoose = require('mongoose');
const jwt = require("jsonwebtoken");
// Connect to MongoDB
mongoose.connect('mongodb://localhost/yourdbname', {
useNewUrlParser: true,
useUnifiedTopology: true
});
const userSchema = new mongoose.Schema({
id: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true
},
name: {
type: String,
required: true
},
password: {
type: String,
required: true
},
plan: {
type: String,
default:
'Standard'
},
avatar: {
type: String,
default:
null
},
});
const User = mongoose.model('User', userSchema);
const JWT_SECRET = process.env.JWT_SECRET;
const JWT_EXPIRES_IN = '7d';
class AuthApi {
async signIn(request) {
const {
email,
password
} = request;
const user = await User.findOne({
email
});
if (!user || (user.password !== password)) {
throw new Error('Please check your email and password');
}
const accessToken = jwt.sign({
userId: user.id
}, JWT_SECRET, {
expiresIn: JWT_EXPIRES_IN
});
return {
accessToken
};
}
async signUp(request) {
const {
email,
name,
password
} = request;
const existingUser = await User.findOne({
email
});
if (existingUser) {
throw new Error('User already exists');
}
const newUser = new User({
id: mongoose.Types.ObjectId(),
email,
name,
password,
plan: 'Standard',
avatar: null,
});
await newUser.save();
const accessToken = jwt.sign({
userId: newUser.id
}, JWT_SECRET, {
expiresIn: JWT_EXPIRES_IN
});
return {
accessToken
};
}
async me(request) {
const {
accessToken
} = request;
const decoded = jwt.verify(accessToken, JWT_SECRET);
const {
userId
} = decoded;
const user = await User.findById(userId);
if (!user) {
throw new Error('Invalid authorization token');
}
return {
id: user.id,
avatar: user.avatar,
email: user.email,
name: user.name,
plan: user.plan
};
}
}
export const authApi = new AuthApi();
I have a unique file that contains all the functions I apply to my mongoose model.
But as I plan to have a lot of functions I would like to have several sub-files that contain these functions.
For example I would like to have a "user functions" sub-file and a "product functions" sub-file.
I can't manage to do the import/export to get this result.
For now I have this file of functions:
const Schemas = require('./Schemas');
mongoose.connect('mongodb://localhost/test', () => {
console.log('Connected to MongoDB');
}, (err) => {
console.log(err);
});
async function createUser(name, email) {
try {
const user = new Schemas({
name,
email
});
await user.save();
console.log('User created');
} catch (e) {
console.log("### ERROR: ",e.message);
}
}
async function createProduct(name, price, userName) {
try {
const product = new Schemas({
name,
price
});
await product.save();
const user = await Schemas.findOne({name: userName});
user.products.push(product);
await user.save();
console.log('Product created');
} catch (e) {
console.log("### ERROR: ",e.message);
}
}
createUser('John', 'john#doe.com');
createProduct('Product_name', 100, 'John');
I would like the createProduct function to be in another file!
I don't know if it is important. This is my Schemas.js file:
const { Schema } = mongoose;
const productSchema = new Schema({
name: String,
price: Number
});
const userSchema = new Schema({
name: String,
email: String,
products: [productSchema]
});
module.exports = mongoose.model('Schemas', userSchema);
server:
const { ApolloServer, gql } = require("apollo-server-express");
const express = require("express");
const mongoose = require("mongoose");
const { userResolvers } = require("./resolvers");
const { typeDefs } = require("./typeDefs");
const startServer = async () => {
await mongoose.connect("mongodb://localhost:27017/school", {
useNewUrlParser: true,
});
const server = new ApolloServer({
typeDefs,
userResolvers,
});
await server.start();
const app = express();
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
);
};
startServer();
resolver
const User = require("./models/userModel").Users;
const userResolvers = {
Mutation: {
addUser: (parent, args) => {
let User = new User({
name: args.name,
email: args.email,
password: args.password,
otp: args.otp,
});
return User.save();
},
},
};
module.exports = { userResolvers };
typedefs
const { gql } = require("apollo-server-express");
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
password: String!
otp: Float!
}
type Mutation {
addUser(name: String!, email: String!, password: String!, otp: Int!): User
updateUser(
name: String!
email: String!
password: String!
otp: Int!
): User
deleteUser(id: ID!): User
}
`;
module.exports = { typeDefs };
here is my graphql code. Here i am trying to create user. But when i am creating a user in graphql it is sending null as response
It is not thowing any error so it is unclear for me .
Please take a look what can be the error
I have attached the screenshot also
Your local definition of User
let User = new User(...);
overwrites the global definition
const User = require("./models/userModel").Users;
This should lead to an exception
Cannot access 'User' before initialization
in the addUser function, which probably causes the null response.
Use a different name for the local definition, e.g., in lower case:
let user = new User({
name: args.name,
email: args.email,
password: args.password,
otp: args.otp,
});
return user.save();
I have the following code.
I want to implement authentication using MongoDB, mongoose, express using typescript.
I am having a typescript issue. I tried declaring type (maybe incorrectly) for findUser and did not resolve. Any tips?
model.ts
import mongoose, { Schema, Document } from 'mongoose';
import bcrypt from 'bcrypt';
export interface IUser extends Document {
username: string;
password: string;
}
const userSchema: Schema = new Schema({
username: {
type: String,
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
});
// tslint:disable-next-line: only-arrow-functions
userSchema.statics.findUser = async function (username, password) {
const user = await User.findOne({ username });
if (!user) {
return;
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return;
}
return user;
};
userSchema.pre<IUser>('save', async function (next) {
const user = this;
if (user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 8);
}
next();
});
const User = mongoose.model<IUser & Document>('User', userSchema);
export default User;
auth.ts (route)
ERROR:Property 'findUser' does not exist on type 'Model<IUser & Document>'.ts(2339)
import express from 'express';
import User from '../models/user-model';
const router = express.Router();
declare module 'express-session' {
// tslint:disable-next-line: interface-name
export interface SessionData {
user: { [key: string]: any };
}
}
router.post('/signin', async (req, res) => {
const { email, password } = req.body;
const user = await User.findUser(email, password);
if (user) {
req.session.user = user._id;
res.json({
message: 'You are successfully login',
auth: true,
});
} else {
res.json({
message: 'Unable to login',
auth: false,
});
}
});
export = router;
You can set a second generic on the mongoose.model() method which describes the model itself.
Here we include all of the properties of Model<IUser> and also add your custom function.
type UserModel = Model<IUser> & {
findUser: (username: string, password: string) => Promise<IUser | undefined>;
}
IUser determines the type for the documents in this model, while UserModel determines the type for the model.
const User = mongoose.model<IUser, UserModel>('User', userSchema);
Now the type for the method is known. user here gets type IUser | undefined;
const user = await User.findUser('joe', 'abcd');
Typescript Playground Link
I am trying to register a new user in my db.
I am using redux as well for this.
I am confused on what the error message is indicating is the problem.
I am confronted with the 500 server error and this line to follow?:
'(intermediate value) is not a constructor'
To give you a deeper understanding as to where this might be occurring, I have pasted some important parts of my code that initiates this sequence of events that lead to this.
Here's my action:
accountApi.register(
email,
password,
firstName,
lastName,
(errObj, resObj) => {
if (errObj) {
return dispatch({
type: REGISTER_USER_FAILED,
payload: new Error(errObj.detail)
});
}
const { token } = resObj;
storage.save(JWT_TOKEN, token, err => {
if (err) {
return dispatch({
type: REGISTER_USER_FAILED,
payload: err
});
}
return dispatch({
type: REGISTER_USER_SUCCESS,
payload: true
});
});
}
);
Here's the api route:
const body = {
email,
password,
firstName,
lastName,
date: today,
};
const ROUTE = "register";
const url = BASE_ROUTE + ROUTE;
client.post({ url, body }, cb);
}
inside API:
async function register(req, res) {
req.checkBody('email').notEmpty().isEmail();
req.checkBody('password')
.notEmpty()
.isAscii()
.isLength({ min: 8 });
req.checkBody('firstName')
.notEmpty()
.isAscii()
.isLength({ min: 3, max: 15 });
req.checkBody('lastName')
.notEmpty()
.isAscii()
.isLength({ min: 3, max: 15 });
});
const validationErrors = await req.getValidationResult();
if (!validationErrors.isEmpty()) {
return res.status(400).send(responses.validatorResponse(validationErrors));
}
const { email, password, firstName, lastName } = req.body;
const userDoc = await accountManager.register(email, password, firstName,
lastName, false);
return res.status(200).send({ token: jwtHelper.createToken(userDoc) });
}
in accountmanger:
async function register(email, password, firstName, lastName, isAdmin) {
const associationDoc = await Association.getDocumentByEmail(email);
if (!associationDoc) {
throw new new Error('Unable to register user. User is not associated with
any organization yet.')();
}
const teamDoc = await
Team.getDefaultTeamForOrganization(associationDoc.organizationId);
const hashedPassword = account.hashPassword(password);
const data = {
firstName: firstName.toLowerCase(),
lastName: lastName.toLowerCase(),
email,
isAdmin,
fullName: `${firstName.toLowerCase()} ${lastName.toLowerCase()}`,
teamId: teamDoc._id,
hashedPassword,
registrationStatus: REGISTRATION_STATUSES.NEW_REGISTRATION,
lastSeen: dates.today(),
};
return User.create(data);
}
And inside user model:
async function create(data) {
return new Model(data).save();
}
I use postman to send the api calls with all the proper info but I keep getting this error message back:
(intermediate value) is not a constructor