I'm trying to build an API using node.js, with sequelize as ORM.
I'm having a problem with the model.create (controller Categoria) of classes that have relationships.
These are the migrations:
migration 'categorias':
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('categorias', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
nome: {
type: Sequelize.STRING,
},
created_at: {
allowNull: false,
type: Sequelize.DATE,
},
updated_at: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('categorias');
},
};
migration 'ensaios':
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('ensaios', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
idCategoria: {
type: Sequelize.INTEGER,
allowNull: false,
references: { model: 'categorias', key: 'id' },
},
nome: {
type: Sequelize.STRING,
},
created_at: {
allowNull: false,
type: Sequelize.DATE,
},
updated_at: {
allowNull: false,
type: Sequelize.DATE,
},
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('ensaios');
},
};
These are the corresponding models:
model 'Categoria':
import Sequelize, { Model } from 'sequelize';
export default class Categoria extends Model {
static init(sequelize) {
super.init({
nome: Sequelize.STRING,
}, {
sequelize,
});
}
static associate(models) {
Categoria.hasMany(models.Ensaio, { foreignKey: 'idCategoria' });
}
}
model 'Ensaio':
import Sequelize, { Model } from 'sequelize';
export default class Ensaio extends Model {
static init(sequelize) {
super.init({
nome: Sequelize.STRING,
}, {
sequelize,
});
}
static associate(models) {
Ensaio.belongsTo(models.Categoria, { foreignKey: 'idCategoria' });
}
}
My app.js:
import dotenv from 'dotenv';
dotenv.config();
import express from 'express';
import categoria from './src/routes/categoria';
import './src/database';
class App {
constructor() {
this.app = express();
this.middlewares();
this.routes();
}
middlewares() {
this.app.use(express.urlencoded({ extended: true }));
this.app.use(express.json());
}
routes() {
this.app.use('/', categoria);
}
}
export default new App().app;
The route 'categoria':
import { Router } from 'express';
import Categoria from '../controllers/Categoria';
const router = new Router();
router.get('/', Categoria.index);
export default router;
The index file that starts sequelize and models
import Sequelize from 'sequelize';
import databaseConfig from '../config/database';
import Categoria from '../models/Categoria';
import Ensaio from '../models/Ensaio';
const models = [Categoria, Ensaio];
const connection = new Sequelize(databaseConfig);
models.forEach((model) => model.init(connection));
finally, the controller Categoria:
import Categoria from '../models/Categoria';
class CategoriaController {
async index(req, res) {
const novaCategoria = await Categoria.create({
nome: 'Farmácia',
});
res.json(novaCategoria);
}
}
export default new CategoriaController();
So, I'm trying to insert an example category into the database, in order to test my API done so far.
However, even having done the migrations correctly, and created the tables (categorias and ensaios), I'm getting an error message that says: "Table 'Projeto_EP.categoria' doesn't exist".
And what I'm finding strange is that the error message appears the name of the table in the singular (categoria) and the table in the database is in the plural (categorias).
I'm really new to programming, and all I've done so far is with the help of videos and classes... I apologize for not having constructed the question in the best way possible. I've been trying to solve this problem for a long time, I hope this amazing community can help me. Thank you very much !
I created a migration (alunos) without any relationships. I migrated it to my database, made the model (Aluno) and used it in my controller to try to insert a 'aluno' example in the database. So in the controller I used Aluno.create() and got success, without any error message. This leads me to believe that I am having problems with the associations between the Essay and Category models.
I hope this information is of help in solving my problem. I thank you all.
Related
I have an error with Sequelize model.
I have two models: Hostel and Faculty. And I need many-to-many relationship between them.
I added to Hostel the next:
Hostel.belongsToMany(Faculty, {
through: 'university.hostels_to_faculties',
sourceKey: 'hostel_id',
targetKey: 'faculty_id',
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
});
Full code source is here: https://github.com/ASUDR/backend/blob/dev/src/db/models/university/Hostel.ts#L41
And when I run this code, I get an error:
/opt/app/node_modules/sequelize/lib/associations/belongs-to-many.js:129
this.sourceKeyField = this.source.rawAttributes[this.sourceKey].field || this.sourceKey;
(here is arrow that points out to field at this.source.rawAttributes[this.sourceKey].field)
TypeError: Cannot read properties of undefined (reading 'field')
at new BelongsToMany (/opt/app/node_modules/sequelize/lib/associations/belongs-to-many.js:129:69)
at Function.belongsToMany (/opt/app/node_modules/sequelize/lib/associations/mixin.js:64:25)
at file:///opt/app/dist/db/models/university/Hostel.js:24:8
at ModuleJob.run (node:internal/modules/esm/module_job:183:25)
at async Loader.import (node:internal/modules/esm/loader:178:24)
at async Object.loadESM (node:internal/process/esm_loader:68:5)
at async handleMainPromise (node:internal/modules/run_main:63:12)
I fixed this error by creating the model for junction table:
import Sequelize, { Optional } from 'sequelize';
import sequelize from '../../sequelize';
const { DataTypes, Model } = Sequelize;
export interface FacultyHostelsAttributes {
id: number;
hostelId: number;
facultyId: number;
}
export interface FacultyHostelsAttributesInput extends Optional<FacultyHostelsAttributes, 'id'> {}
export interface FacultyHostelsAttributesOutput extends Required<FacultyHostelsAttributes> {}
export class FacultyHostels
extends Model<FacultyHostelsAttributes, FacultyHostelsAttributesInput>
implements FacultyHostelsAttributes {
public id: number;
public hostelId: number;
public facultyId: number;
}
FacultyHostels.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
hostelId: {
type: DataTypes.INTEGER,
field: 'hostel_id',
allowNull: false
},
facultyId: {
type: DataTypes.INTEGER,
field: 'faculty_id',
allowNull: false
}
}, {
sequelize,
schema: 'objects',
modelName: 'faculty_hostels',
timestamps: false
});
And changing belongsToMany arguments:
Hostel.belongsToMany(Faculty, {
through: FacultyHostels,
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
});
Faculty.belongsToMany(Hostel, {
through: FacultyHostels,
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
});
more: https://github.com/ASUDR/backend/blob/main/src/db/models/university/Hostel.ts
I am using express with admin-bro and its plugin #admin-bro/upload to store uploaded image file in server.
The AdminBro object is initialized with the following AdminBroOptions
const adminBro = new AdminBro({
resources: [
{
resource: Furniture,
options: {
properties: {
'_id': {
isVisible: { list: false, filter: true, show: true, edit: false }
},
image: {
isVisible: false
}
}
},
features: [uploadFeature({
provider: { local: { bucket: path.join(__dirname, 'public', 'images', 'furniture') } },
properties: {
key: 'image.path',
bucket: 'image.folder',
mimeType: 'image.type',
size: 'image.size',
filename: 'image.filename',
file: 'uploadFile'
}
})]
}
],
branding: {
companyName: "Fran's Furniture"
}
})
The folder structure:
folder structure
I followed this tutorial mostly for admin-bro configuration: https://itnext.io/the-easiest-and-fastest-way-to-upload-files-in-node-js-4b49e0123190
I have also added app.use('/public', express.static('public')) in server.js file
But I run into this error while uploading image file:
(node:153012) ExperimentalWarning: The fs.promises API is experimental
Error: EPERM: operation not permitted, mkdir 'F:'
If I change the bucket property to just 'public':
provider: { local: { bucket: 'public' } }
The following error pops up (notice the 'public' folder is in F:/ not F:/path-to-project-folder:
(node:163580) ExperimentalWarning: The fs.promises API is experimental
Error: EXDEV: cross-device link not permitted, rename 'C:\Users\Lenovo\AppData\Local\Temp\upload_4ca3c78a0517022a555815196102aee6' -> 'F:\public\5f98496b31e9d37efe9a2584\1.jpg'
The furniture model (if needed):
const Image = new mongoose.Schema({
path: String,
type: String,
size: Number,
folder: String,
filename: String
})
const FurnitureSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
status: {
type: String,
enum: ['show', 'hide'],
default: 'show',
required: true
},
condition: {
type: String,
enum: ['new', 'used'],
required: true
},
image: Image,
category: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Category'
}
})
So in my guess, the issue is mostly because of incorrect configuration of bucket option. Any help would be appreciated.
The project path (if needed): F:\Tutorials\Self-Projects\frans-node
I solved this by writing a custom base provider myself and copying+removing the file manually:
import { BaseProvider } from '#admin-bro/upload'
import { ActionContext, UploadedFile } from 'admin-bro'
import { promises, existsSync } from "fs"
import { resolve, dirname } from "path"
export class UploadProvider extends BaseProvider {
assetPath: string
constructor(bucket: string, assetPath: string) {
// it requires bucket as a parameter to properly pass it to other methods
super(bucket)
this.assetPath = assetPath
}
async upload(file: UploadedFile, key: string, context: ActionContext): Promise<any> {
const fullPath = resolve(this.assetPath, key)
const dirPath = dirname(fullPath)
if (!existsSync(dirPath)) {
await promises.mkdir(dirPath, { recursive: true })
}
await promises.copyFile(file.path, fullPath)
await promises.unlink(file.path)
return key
}
async delete(key: string, bucket: string, context: ActionContext): Promise<any> {
const filePath = resolve(this.assetPath, key)
if (existsSync(filePath)) {
await promises.unlink(filePath)
const dirPath = dirname(filePath)
const otherFiles = await promises.readdir(dirPath)
if (otherFiles && otherFiles.length == 0) {
await promises.rmdir(dirPath)
}
}
}
path(key: string, bucket: string, context: ActionContext): Promise<string> | string {
return "/" + bucket
}
}
And then, using it like:
// ...
const provider = new UploadProvider("assets", ASSET_PATH)
// ...
features: [
uploadFileFeature({ provider, ... })
]
I have an Apollo GraphQL projects where I have created my own Query and Mutations. I have done using mock data and Query and Mutation works fine. But when I am trying to do with Sequelize ORM, I am getting the error
"TypeError: Cannot read property 'getListings' of undefined",
" at listings (/home/ayman/Desktop/apollo-graphql/graphql-app/functions/graphql.js:50:19)",
" at field.resolve (/home/ayman/Desktop/apollo-graphql/graphql-app/node_modules/graphql-extensions/dist/index.js:134:26)"
Query and Mutations in graphql.js:
const { ApolloServer, gql} = require("apollo-server-lambda");
const { Listing, User } = require("../db");
const jwt = require("jsonwebtoken");
const typeDefs = gql`
type Query {
listings: [Listing!]!
}
type Mutation {
createListing(input: CreateListingInput!): Listing!
}
input CreateListingInput {
title: String!
description: String
url: String!
notes: String
}
type Contact {
id: ID!
name: String!
company: Company
email: String
notes: String
}
type Company {
id: ID!
name: String!
logo: String
listings: [Listing!]!
url: String
}
type Listing {
id: ID!
title: String!
description: String
url: String!
notes: String
company: Company
contacts: [Contact!]!
}
`;
const resolvers = {
Query: {
listings(_, __, { user }) {
return user.getListings();
},
},
Mutation: {
createListing(_, { input }, { user }) {
return Listing.create({ ...input, userId: user.id });
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
exports.handler = server.createHandler();
I have Sequilize along with Postgres database:
db.js
const Sequelize = require("sequelize");
const sequelize = new Sequelize(process.env.DB_CONNECTION_STRING, {
dialect: "pg",
dialectModule: require('pg'),
dialectOptions: {
ssl: true,
},
});
class User extends Sequelize.Model {}
User.init(
{
email: Sequelize.STRING,
password: Sequelize.STRING,
},
{
sequelize,
modelName: "user",
}
);
class Listing extends Sequelize.Model {}
Listing.init(
{
title: Sequelize.STRING,
description: Sequelize.TEXT,
url: Sequelize.STRING,
notes: Sequelize.TEXT,
},
{
sequelize,
modelName: "listing",
}
);
Listing.belongsTo(User);
User.hasMany(Listing);
exports.sequelize = sequelize;
exports.User = User;
exports.Listing = Listing;
Github Link
Run using netlify dev
Go to URL: http://localhost:8888/.netlify/functions/graphql
Sample GraphQL query
{
listings {
id
title
description
url
company {
name
url
}
}
}
return user.getListings();
you probably mean User, because user is undefined
I see, you are trying to access user object from context. Please check the context definition. It should return an object containing user object explicitly.
Update
This morning I wanted to use another inbuilt sequelize function and suddenly the belongsTo() and hasMany() functions were available and working on some models -not all! These models have all exactly the same pattern and only differ in name.
For example hasOne is not available for topicClass
I didn't change anything else. Except I restarted intelliJ. I did restart it before I opened this question and always had the same issue. So I think this issue is IDE based! Still would appreciate a tip how to avoid this
I have an APP using sequelize with SQLite.
When I want to setup the associations in my db-controller.js it tells me that the association functions like belongsTo and hasmany from the docu are not available.
When I execute the setupAssociations function and check the db later there are the models but no associations set.
Here the db-controller.js (more info from my side after the models at the bottom!)
const Sequelize = require('sequelize')
const userModel = require('../model/user')
const subjectModel = require('../model/subject')
// eslint-disable-next-line no-unused-vars
const initUser = userModel.user.initUser()
// eslint-disable-next-line no-unused-vars
const initSubject = subjectModel.subject.initSubject()
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: './user.sqlite',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
logging: false
})
sequelize.sync()
function setupAssociations(){
// user + subject
userModel.user.userClass.hasMany(subjectModel.subject.subjectClass)
subjectModel.subject.subjectClass.belongsTo(userModel.user.userClass)
// subject + topic
}
function testAssociations(){
setupAssociations()
}
testAssociations()
and my two models
const Sequelize = require('sequelize')
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: '../controller/user.sqlite',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
logging: false
})
sequelize.sync()
/**
* User Model
*/
class User extends Sequelize.Model {
}
/**
* Init user model
*/
function initUser () {
//TODO optional realize with sequelize.transaction
User.init(
// attributes
{
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING,
allowNull: false
},
email: {
type: Sequelize.STRING,
allowNull: false
},
title: {
type: Sequelize.STRING,
allowNull: false
},
password: {
type: Sequelize.TEXT,
allowNull: false
}
},
// options
{
sequelize,
modelName: 'user'
}
)
}
/**
* Makes functions available globally
*/
exports.user = {
initUser: initUser,
userClass: User
}
and Subject
const Sequelize = require('sequelize')
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: '../controller/user.sqlite',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
logging: false
})
sequelize.sync()
/**
// * Subject Model
*/
class Subject extends Sequelize.Model {
}
/**
* Initialises subject model
*/
function initSubject () {
Subject.init(
// attributes
{
subjectName: {
type: Sequelize.STRING,
allowNull: false
}
},
// options
{
sequelize,
modelName: 'subject'
}
)
}
/**
* Makes functions globally available
*/
exports.subject = {
initSubject: initSubject,
subjectClass: Subject
}
If I create another class like class TestClass extends Sequelize.Model in the user.js file and call hasMany with belongsTo within the initUser function and check the db after, then everything is as I want it to.
So what I do here wrong please?
I figured out that I had from another project a script running adding to the comment of the export function
#type {{initTopic: (function(): Promise<*>), topicClass: Topic}}
This caused the issue. Removing it made all sequelize functions appearing again.
In code from 2016 using sequelize ORM, I see model types defined with this pattern:
module.exports = function(sequelize, DataTypes) {
const Tasks = sequelize.define("Tasks", { id: {
type: DataTypes.INTEGER,
[ ...etc.]
However in the current sequelize docs you see most prominently documented: Sequelize.INTEGER (or other type then integer).
At the same time in the current docs I find also DataTypes still documented/used: here.
On same page the Sequelize.INTEGER is used..., is that only for deferrables or something?
I tried to find whether this altered over time or something but could not find it.
When Sequelize.INTEGER is 'current solution' could I just alter above code into:
module.exports = function(sequelize, Sequelize) {
const Tasks = sequelize.define("Tasks", { id: {
type: Sequelize.INTEGER,
[ ...etc.]
Or would using Sequelize as argument somehow make this fail?
The second parameter in both of them is just the sequelize package itself You can use any of them which is on you what you want to use
const Sequelize = require('sequelize');
You'll notice in your index.js of models (if you set up as suggested) that you do something like the below, where you are passing in sequelize as the second argument.
const model = require(path.join(__dirname, file))(sequelize, Sequelize);
This exposes the data types. It doesn't matter what you call it. For example I am calling it abc in below code you can use any name
module.exports = (sequelize, abc) => {
const Driver = sequelize.define('Driver', {
firstName: {
type: abc.STRING(),
allowNull: false
},
last_name: {
type: abc.TEXT,
allowNull: true
},
email: {
type: abc.TEXT,
allowNull: false
},
password: {
type: abc.TEXT,
allowNull: true
}
Same with migrations.