简体   繁体   中英

Cannot read properties of undefined (reading 'findOne') | apollo-server-express | sequelize

I'm being driven slowly mad by this issue.

Using a local Apollo Server instance being accessed by Apollo Studio, I am attempting a simple mutation, createUser, and this issue is arising. What have I misunderstood?

Am I incorrectly consuming the context I provided in the server's creation? Or incorrectly accessing this model, maybe? Not sure!

Here is the error showing in Apollo Studio, followed by my files:

{
  "errors": [
    {
      "message": "Cannot read properties of undefined (reading 'findOne')",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "createUser"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "stacktrace": [
            "TypeError: Cannot read properties of undefined (reading 'findOne')",
            "    at Object.createUser (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/graphql/resolvers/user.js:22:46)",
            "    at field.resolve (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/apollo-server-core/dist/utils/schemaInstrumentation.js:56:26)",
            "    at executeField (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/graphql/execution/execute.js:479:20)",
            "    at /Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/graphql/execution/execute.js:375:22",
            "    at promiseReduce (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/graphql/jsutils/promiseReduce.js:23:9)",
            "    at executeFieldsSerially (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/graphql/execution/execute.js:371:43)",
            "    at executeOperation (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/graphql/execution/execute.js:345:14)",
            "    at execute (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/graphql/execution/execute.js:136:20)",
            "    at execute (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/apollo-server-core/dist/requestPipeline.js:205:48)",
            "    at processGraphQLRequest (/Volumes/T7 Touch/Projects/PassTheArt/pass-the-art-server/node_modules/apollo-server-core/dist/requestPipeline.js:148:34)"
          ]
        }
      }
    }
  ],
  "data": null
}
// ./server.js
require('dotenv').config();
import express from 'express';
import db from './db';
import resolvers from './graphql/resolvers';
import typeDefs from './graphql/typeDefs';
import http from 'http';
import { ApolloServer } from 'apollo-server-express';

async function startApolloServer(){
    const server = new ApolloServer({
        typeDefs, 
        resolvers,
        introspection: true,
        playground: true,
        context: async() => {
            return {
                db
            }
        }
    });
    
    const app = express();
    const httpServer = http.createServer(app);
    
    server.start().then(res=>{
        server.applyMiddleware({app, path: '/graphql'});
        db.sequelize.sync({force: true}).then(async()=>{
            console.log('database synced');
        });
        httpServer.listen({port: process.env.PORT}, ()=>{
            console.log(`Apollo Server is ready at http://localhost:${process.env.PORT}/graphql`)
        })
    })
}

startApolloServer();

// ./graphql/resolvers/user.js
import { UserInputError } from "apollo-server-express";
import { Op } from "sequelize";

export default {
    Query: {
        // ! This query is for the logged in user
        me: async(root, args, {db, me}, info) => {
            const user = await db.user.findByPk(me.id);
            return user;
        },
        // ! This query returns all users
        users: async(root, args, {db}, info) => {
            const users = await db.user.findAll();
            if (!users) throw new Error('No users found')
            return users;
        }
    },
    Mutation: {
        // ! This mutation creates a new user
        createUser: async(root, {input}, {db}) => {
            const {email} = input;
            const userExists = await db.user.findOne({
                where: {
                    [Op.eq]: [{email}]
                }
            })
            if (userExists) {
                throw new Error('A user with this email already exists');
            }
            const user = await db.user.create({
                ...input
            });
            return user;
        },
        // ! 
        login: async(root, {email, password}, {db}, info) => {
            const user = await db.user.findOne({
                where: {email},
            });
            if(!user) throw new UserInputError(`User ${email} does not exist`);
            const isValid = await user.validatePassword(password);
            if(!isValid) throw new UserInputError(`Password is incorrect`);
            return user;
            
        }
    } 
}
// ./db.js
require('dotenv').config();
import fs from 'fs';
import path from 'path';
import { Sequelize } from 'sequelize';

const basename = path.basename(__filename);
const db = {};

const sequelize = new Sequelize(
    process.env.POSTGRES_DB,
    process.env.POSTGRES_USER,
    process.env.POSTGRES_PASSWORD,
    {
        host: process.env.POSTGRES_HOST,
        port: process.env.POSTGRES_PORT,
        dialect: 'postgres'
    }
);

sequelize.authenticate()
.then(console.log(()=>'Connection has been established successfully.'))
.catch(e=>console.error('Unable to connect to the database:', e));

const modelPath = path.join(__dirname, '/models');
fs.readdirSync(path.join(modelPath))
    .filter((file)=>
        file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js'
    )
    .forEach((file)=>{
        const model = sequelize.define(path.join(modelPath, file));
        db[model.name] = model;
    });
    
Object.keys(db).forEach((modelName)=>{
    if (db[modelName].associate){
        db[modelName].associate(db);
    }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

export default db;
// ./models/User.js
import bcrypt from 'bcryptjs';

export default (sequelize, DataTypes) => {
    const User = sequelize.define(
        'user',
        {
            name: {
                type: DataTypes.STRING,
                allowNull: false,
            },
            email: {
                type: DataTypes.STRING,
                allowNull: false,
                unique: true, 
                validate: {
                    isEmail: {
                        args: true,
                        msg: 'Invalid email'
                    },
                },
            },
            password: {
                type: DataTypes.STRING,
                allowNull: false,
            },
        },
        {
            freezeTableName: true,
        },
    );

    User.findByLogin = async (login) => {
        let user = await User.findOne({
            where: {email: login},
        });
        return user;
    };

    User.beforeCreate(async (user) => {
        if (user.password){
            user.password = await user.generatePasswordHash();
        }
    });

    User.prototype.updatePasswordHash = async function (password) {
        const saltRounds = 10;
        return await bcrypt.hash(password, saltRounds);
    };

    User.prototype.updatePasswordHash = async function () {
        const saltRounds = 10;
        return await bcrypt.hash(this.password, saltRounds);
    };

    User.prototype.validatePassword = async function (password) {
        return await bcrypt.compare(password, this.password);
    };

    return User;
}
// ./graphql/typedefs/User.js
import { gql } from "apollo-server-express";

export default gql`
    #---------------------------------------
    # TYPES
    #---------------------------------------
   
    type User {
        id: ID
        name: String!
        email: String!
    }

    #---------------------------------------
    # QUERIES
    #---------------------------------------
    
    extend type Query {
        me: User
        users: [User!]
    }

    #---------------------------------------
    # MUTATIONS
    #---------------------------------------

    extend type Mutation {
        createUser(input: CreateUserInput!): User!
        login(email: String!, password: String!): User!
        logout: User!
    }

    #---------------------------------------
    # MUTATIONS
    #---------------------------------------

    input CreateUserInput {
        name: String!
        email: String!
        password: String!
    }
`

You need to correct where option from

where: {
  [Op.eq]: [{email}]
}

to

where: {
  email
}

just like you did in the login mutation.

I managed to get db.user.findOne defined properly. I was not properly using sequelize.define() in db.js , and I have now rewritten the pesky section as such:

const modelPath = path.join(__dirname, '/models');
fs.readdirSync(path.join(modelPath))
    .filter((file)=>
        file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js'
    )
    .forEach((file)=>{
        const modelFile = path.join(modelPath, file);
        const modelExport = require(modelFile);
        if (! modelExport) throw new Error ('Error accessing model declaration file: ', modelFile)
        const model = modelExport.default(sequelize);
        db[model.name] = model;
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM