无法读取未定义的属性(读取 'findOne')|阿波罗服务器快递 |续集
Cannot read properties of undefined (reading 'findOne') | apollo-server-express | sequelize
我被这个问题慢慢逼疯了。
使用 Apollo Studio 访问的本地 Apollo Server 实例,我正在尝试一个简单的变更,createUser,并且出现了这个问题。我误会了什么?
我是否错误地使用了我在创建服务器时提供的上下文?或者可能错误地访问了这个模型?不确定!
这是 Apollo Studio 中显示的错误,后面是我的文件:
{
"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!
}
`
您需要更正来自
的where
选项
where: {
[Op.eq]: [{email}]
}
到
where: {
email
}
就像你在 login
突变中所做的那样。
我设法正确定义了 db.user.findOne
。我在 db.js
中没有正确使用 sequelize.define()
,现在我已经重写了讨厌的部分:
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;
});
我被这个问题慢慢逼疯了。
使用 Apollo Studio 访问的本地 Apollo Server 实例,我正在尝试一个简单的变更,createUser,并且出现了这个问题。我误会了什么?
我是否错误地使用了我在创建服务器时提供的上下文?或者可能错误地访问了这个模型?不确定!
这是 Apollo Studio 中显示的错误,后面是我的文件:
{
"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!
}
`
您需要更正来自
的where
选项
where: {
[Op.eq]: [{email}]
}
到
where: {
email
}
就像你在 login
突变中所做的那样。
我设法正确定义了 db.user.findOne
。我在 db.js
中没有正确使用 sequelize.define()
,现在我已经重写了讨厌的部分:
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;
});