Im trying to extend a Mongoose Model using ES6 syntax. While I can call successfully find({}) to retrieve data from mongo database, I am not able to call save() to save data. Both are executed inside the Model.
The error returned is Error: TypeError: this.save is not a function
const mongoose = require('mongoose')
const {Schema, Model} = mongoose
const PersonSchema = new Schema(
{
name: { type: String, required: true, maxlength: 1000 }
},
{ timestamps: { createdAt: 'created_at', updatedAt: 'update_at' } }
)
class PersonClass extends Model {
static getAll() {
return this.find({})
}
static insert(name) {
this.name = 'testName'
return this.save()
}
}
PersonSchema.loadClass(PersonClass);
let Person = mongoose.model('Persons', PersonSchema); // is this even necessary?
(async () => {
try {
let result = await Person.getAll() // Works!
console.log(result)
let result2 = await Person.insert() // FAILS
console.log(result2)
} catch (err) {
throw new Error(err)
}
})()
Im using:
Nodejs 7.10
mongoose 5.3.15
This is normal. You're trying to access a non static method from a static method.
You need to do something like this:
static insert(name) {
const instance = new this();
instance.name = 'testName'
return instance.save()
}
Some working example:
class Model {
save(){
console.log("saving...");
return this;
}
}
class SomeModel extends Model {
static insert(name){
const instance = new this();
instance.name = name;
return instance.save();
}
}
const res = SomeModel.insert("some name");
console.log(res.name);
Here is an example of what works and what doesn't work.
class SomeParentClass {
static saveStatic(){
console.log("static saving...");
}
save(){
console.log("saving...");
}
}
class SomeClass extends SomeParentClass {
static funcStatic(){
this.saveStatic();
}
func(){
this.save();
}
static funcStaticFail(){
this.save();
}
}
//works
SomeClass.funcStatic();
//works
const sc = new SomeClass();
sc.func();
//fails.. this is what you're trying to do.
SomeClass.funcStaticFail();
Related
I'm learning JavaScript with Express.js Framework. I'm trying to create a simple project for a restaurant (just for learning purposes), and I'm trying to create the CRUD of ingredients. I created the repository for all Prisma (ORM) requests, the service with business logic, and a controller for request/response handling. I wish to know if there is a better way of instantiating the service and repository on my controller. I'm doing that for each method. It worked, but I'm repeating this code block a lot.
This is IngredientRepository:
const prisma = require("../../prisma/client");
class IngredientRepository {
async create({ name, price, image }) {
return await prisma.ingredient.create({
data: {
name,
price,
image,
},
});
}
async findByName({ name }) {
return await prisma.ingredient.findUnique({ where: { name } });
}
async findById({ id }) {
return await prisma.ingredient.findUnique({
where: { id },
});
}
async updateImage({ id, image }) {
return await prisma.ingredient.update({
where: { id },
data: {
image,
},
});
}
}
module.exports = IngredientRepository;
IngredientService:
const Error = require("../middlewares/Error");
const DiskStorage = require("../providers/DiskStorage");
class IngredientService {
constructor(ingredientRepository, userRepository) {
this.ingredientRepository = ingredientRepository;
this.userRepository = userRepository;
}
async create({ name, price, image, loggedUser }) {
const userIsAdmin = await this.userRepository.findById({ loggedUser });
if (userIsAdmin.admin) {
throw Error("", 401);
}
const ingredient = await this.ingredientRepository.findByName({
name: name.toLowerCase(),
});
if (ingredient) {
throw new Error("Ingredient already exists");
}
return await this.ingredientRepository.create({
name: name.toLowerCase(),
price,
image,
});
}
async updateImage({ id, image }) {
const diskStorage = new DiskStorage();
const ingredient = await this.ingredientRepository.findById({ id: id.id });
if (!ingredient) {
throw new Error("This ingredient doesn't exist", 401);
}
if (ingredient.image) {
await diskStorage.deleteFile(ingredient.image);
}
const filename = await diskStorage.saveFile(image);
ingredient.image = filename;
const updatedIngredient = await this.ingredientRepository.updateImage({
id: id.id,
image: ingredient.image,
});
return updatedIngredient;
}
}
module.exports = IngredientService;
IngredientController:
const IngredientRepository = require("../repositories/IngredientRepository");
const UserRepository = require("../repositories/UserRepository");
const IngredientService = require("../services/IngredientService");
class IngredientController {
async create(request, response) {
const ingredientRepository = new IngredientRepository();
const userRepository = new UserRepository();
const ingredientService = new IngredientService(
ingredientRepository,
userRepository
);
const loggedUser = request.user.id;
const { name, price, image } = request.body;
const ingredientCreated = await ingredientService.create({
loggedUser,
name,
price,
image,
});
return response.json(ingredientCreated);
}
async updateImage(request, response) {
const ingredientRepository = new IngredientRepository();
const userRepository = new UserRepository();
const ingredientService = new IngredientService(
ingredientRepository,
userRepository
);
const loggedUser = request.user.id;
const id = request.params;
const image = request.file.filename;
const ingredientWithImageUpdated = await ingredientService.updateImage({
loggedUser,
id,
image,
});
return response.json(ingredientWithImageUpdated);
}
}
module.exports = IngredientController;
It is possible to create your dependencies in constructor of IngredientController and then use that objects in methods.
Let me show an example:
class IngredientRepository {
findByName() {
return 'findByName: ' + Date.now();
}
}
and:
class IngredientController {
constructor (){
this.ingredientRepository = new IngredientRepository();
}
create(){
// you can use your object here
console.log(this.ingredientRepository.findByName())
}
update(){
// you can use your object here
console.log(this.ingredientRepository.findByName())
}
}
An example:
class IngredientRepository {
findByName() {
return 'findByName: ' + Date.now();
}
}
class IngredientController {
constructor (){
this.ingredientRepository = new IngredientRepository();
}
create(){
console.log(this.ingredientRepository.findByName())
}
update(){
console.log(this.ingredientRepository.findByName())
}
}
const ingredientController = new IngredientController();
console.log(ingredientController.create())
console.log(ingredientController.update())
I created a base class that parses a json file:
const fs = require("fs");
class DataRepository {
constructor() {
this.filename = "cales_data.json";
try {
fs.accessSync(this.filename);
} catch (error) {
console.error("data does not exist");
}
}
async getAll() {
// Open the file called this.filename
return JSON.parse(
await fs.promises.readFile(this.filename, {
encoding: "utf-8",
})
);
}
async getOneBy(filters) {
const records = await this.getAll();
for (let record of records) {
let found = true;
for (let key in filters) {
if (record[key] !== filters[key]) {
found = false;
}
}
if (found) {
return record;
}
}
}
}
module.exports = DataRepository;
I don't care for its methods at this point. I just want to be able to access the contents of this.filename = "cales_data.json";
Something like this perhaps:
const DataRepository = require("./repositories/data");
class Address extends DataRepository {
constructor() {
super();
this.Address = DataRepository.Address;
this.Latitude = DataRepository.Latitude;
this.Longitude = DataRepository.Longitude;
}
}
module.exports = Address;
but obviously the above gives me undefined. Is it possible to access those properties inside that JSON file in that base class this way? If so, how?
Simple answer: no. "This" can never result to anything than a value on the class memory stack. You have to handle this different:-)
I'm trying to make a generic repository, I'm using MongoDB for this, I tried to import it and use it in other classes but it didn't work.
So what I need is to make the repository class available through all the application, and every data that needs a db access should be able connect with the repository.
In this scenario
// userSchema.js
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
// repository.js
class Repository {
constructor(dbCollection) {
this.dbCollection = dbCollection;
}
get(_id) {
return this.dbCollection.findById({ _id });
}
}
module.exports = Repository;
// user.repository.js
const Repository = require('./repository');
class UserRepository extends Repository {
constructor(user) {
super({
dbCollection: user
});
}
}
module.exports = UserRepository;
// UserController.js
const UserRepository = require('./user.repository.js');
class UserController {
async find(req, res) {
const user = await UserRepository.get(req.params.id);
return res.status(200).json(user);
}
}
module.exports = new UserController();
// mongo.js
const mongoose = require('mongoose');
const connectionOptions = {
useNewUrlParser: true,
useFindAndModify: false
};
class Database {
constructor() {
this.connect();
}
connect() {
return mongoose.connect(process.env.MONGO_URL, connectionOptions);
}
}
module.exports = new Database();
I tried to do something like:
const UserRepository = require('./user.repository.js');
const schemaUser = require('./schemaUser');
class UserController {
async find(req, res) {
const test = new UserRepository(schemaUser)
const user = await test.get(req.params.id);
return res.status(200).json(user);
}
}
module.exports = new UserController();
But it didn't work
In JavaScript, how to do getter to object in the root level?
I mean how to create getter for this object:
const userModel = {
get() { return { foo: true, ... }; }
}
and access to it by userModel.foo; // true. the code gives me userModel.get().. which not expect what I need.
The idea is go invoke get when I access to userModel.
Is it possible to do with js?
I am using the typeorm library and I have models for example User.
typeorm is able me to use this model by using this syntax:
import { getConnection } from 'typeorm';
const b = await getConnection().getRepository(User).findOne({...})...
now I want to create UserModel that do this:
getConnection().getRepository(User)
as:
export const userModel = {
get() { return getConnection().getRepository(User) }
}
so can I use it as:
userModel.findOne({...});
And I can't just use:
export const userModel = getConnection().getRepository(User);
Because the connection is not initialized yet, so it throws an error.
Yes, it is possible using Proxy:
const getConnection = (model) => ({
getRepository: (model) => {
return { findOne: () => (model) }
}
});
const User = {
//...user model,
name: "Jonh Smith"
}
class ProxyModel {
constructor(model) {
this.model = model;
return new Proxy({}, {get: (function(target, name) {
const repo = getConnection().getRepository(this);
if(name in repo) {
return repo[name];
}
}).bind(this.model)})
}
}
const userModel = new ProxyModel(User);
console.log(userModel.findOne());
Make sure you define getter properly:
const userModel = {
get foo() { return { foo: true }; }
}
console.log(userModel.foo)
so, i am very new to node and express, i ran through a problem like
here i am importing all other js files like
import { User } from './user.js';
class App {
constructor() {
this.init();
}
async init() {
this.user = await new User();
this.team = await new Team();
this.navbar = new Navbar();
this.tree = new Tree();
this.settings = await new Settings();
this.board = await new Board();
this.ping();
return this;
}
ping() {
//some functionality
}
}
now creating the object here
app = await new App();
console.log('app', app);
this gives me
app > AppĀ {}
on clicking the > i am getting this
>user: User {username: "someusername", roles: Array(1), settings: undefined}
>navbar: Navbar {timerIsRunning: false}
how can i access the properties like app.user also JSON.stringify gives me blank {}
Replace constructor in App class by next:
constructor() {
return (async () => {
return await this.init();
})();
}
now creating the object here
(async () => {
const app = await new App();
console.log("app", app);
})();