MongoDB and Mongoose - Create a Model And A Brief Explanation of Javascript Async behavior -- Callback Functions and Handler Functions

I’m stuck the whole night at the database challenge 2) Create a ‘Person’ Model.

The error message appears as “Person Model is not correct”

I’ve tried,

var Person = new Schema({
  name: {
    type: String,
    required: true
  },
  age: Number,
  favoriteFoods: [String]
});

Also ,

var personSchema = new Schema({
  name: {
    type: String,
    required: true
  },
  age: Number,
  favoriteFoods: [String]
});

var Person = mongoose.model('person', personSchema);

i am confused about what the requirement really want

Create a person having this prototype :

  • Person Prototype -

name : string [required]

age : number

favoriteFoods : array of strings (*)

Use the mongoose basic schema types. If you want you can also add

more fields, use simple validators like required or unique,

and set default values.

4 Likes

I’ve just figured out the problem. In case anyone struggles the same. The correction is to capitalise the ‘Person’

var Person = mongoose.model('person', personSchema);
4 Likes

hello where i put the commands of the database in the client server ?

The database is just a javascript object. If you are forking the FCC glitch template project, you can initiate the MongoDB and mongoose interface, in your myApp.js script.

First, add the packages of MongoDB and Mongoose to your package.js. Then require the mongoose interface in your myApp.js. i.e. const mongoose = require("mongoose");

As shown in thequick start guide of mongoose,

 // change the link to your db address
mongoose.connect('mongodb://localhost/test');
// connect to the server
var db = mongoose.connection; 
// check connection errors
db.on('error', console.error.bind(console, 'connection error:'));
// Once established connection
db.once('open', function() {
  // Do things here
});

From this " Create a ‘Person’ Model" we basic starters decode the autor have meaned model called ‘Person’:roll_eyes:

I can’t understand C]RUD Part I - CREATE part. Please could someone explain what are they want in simple words? And what should we do?

Note: Glitch is a real server, and in real servers the interactions with the db happen in handler functions. These function are executed when some event happens (e.g. someone hits an endpoint on your API). We’ll follow the same approach in these exercises. The done() function is a callback that tells us that we can proceed after completing an asynchronous operation such as inserting, searching, updating or deleting. It’s following the Node convention and should be called as done(null, data) on success, or done(err) on error.

Warning - When interacting with remote services, errors may occur !
/* Example */
var someFunc = function(done) {
//... do something (risky) ...
if(error) return done(error);
done(null, result);
};```

The template/instruction on the Glitch is probably more intuitive to follow.

Do make sure you set up your development environment on MLab and Glitch following the instructions. If you look the app.js (the file you need to complete), the comments give very thorough instructions on how to complete each section.

After you complete each section, you can submit the link to the corresponding page to get the lovely green check mark :slight_smile:.

2 Likes

In terms of what you quoted above, I’ll try to explain the terms in more details.

When you actually developing a server, in most cases, you’d first creating a local development environment on your laptop/desktop. Yes, server(Nodejs) and databases(like MongoDB) can run on local machines. This local server doesn’t have much difference to the one you’ll use in production. For instance, many Nodejs server are simply hosted on Linux machines.

However, for example, if I am developing several different servers, they’d likely all have different Node versions and database versions. Switching versions is really a pain. So people who developed stackoverflow developed Glitch. It hosts a virtual sever running Node for you, and you can have as many project(instance) as you like and each can be different, which made sharing really easy. Even better is that, with zero configuration, you can acess it from, i.e. your phone. Thus it says,

Note: Glitch is a real server".

In real-life, we’d rarely use database on itself, but use database to store, manipulate and retrive data from a database. The data then be handled by the server to produce a service. Thus it says,

and in real servers the interactions with the db happen in handler functions.

So for example, a user may make an API request, your server then received that request, on a Node server, the request will be handled by a Route, in that route, you wrote all the magics which often including quering a database for data. However, when your quering a database, there are many chances when errors occure. For example, database can have internal errors, the connection between your server and the database can be lost. Thus, when you quering a database, there is a need to handle the error. Also, the whole nature of Javascript is its asynchronous, for example, after the server send a query to a database, it won’t wait this response instead that it’ll deal the next request. Hence, we use a lot of callbacks(the legacy way) or async/promise(since last few years), to handle the data once it returned from the database.

The example function it’s shown, is just a generic pattern of writting callbacks.

4 Likes

Thank you for explanation!

  var ivan = new Person({
    name: "Ivan",
    age: 22,
    favoriteFoods: ['Pancake', 'Fried egg']
  });

  ivan.save((err, data) => {    
    if (err)
      return done(err);
    return done(null, data);
  });
}; ``` 

Where from   err and data arguments passed  to save function?   done is callback function?

I’ll briefly explain the pattern of a callback function, before I answer the particular function.

First of all, I wish you to understand that a callback function is nothing but a pattern to achieve asynchronous function. And the callback function that you’re passing around is just a normal function.

As I mentioned above, callback is a legacy “workaround” that’s still been widely used. This defacto pattern was adopted by almost all Javascript developer in early ages, when the Javascript language specification(ECMAscript) doesn’t have an official support for async function. It has been gradually replaced by promise and async in recent years. You’ll more or less get to this part when you work on a new website after completing this course. However, even now, the most popular Expressjs server framework is still written in callbacks. Thus the reseaon FCC’s course introduce the callback first.

The callback function has a very simple pattern. It’s nothing but a function that’s been passed (as a parameter) into another function (which is executed asynchronously). I’ll write it in the way of function declaration to make it clear, however, its the same if you write it in anonymous function.

For example, you or some framework is implimenting a feature called asyncFunction that’s can be time consuming. Thus, you wish this be asynchronously executed and doesn’t block your server. Then you impliment this async function with a callback. As you can see that callback is just a parameter.

function asyncFunctionCreateFruits (callback) {
  // You will declare some variables
  let apple, banana;
  
  // You will perform some magic
  // Blah, bleb, blahblah...
  // It could takes awhile
  // ...
  // ...

  // Finally, the magic finished,
  // You will let the passed callback perform the rest work
  // And you'll pass apple and banana to the callback
  callback(apple, banana);
}

// I am a chimpanzee
function handleFruits (fruit, moreFruit) {
  // I eat fruit
  console.log("eat", fruit)
  console.log("eat", moreFruit)
}

// So when you actually call the asyncFunction
// You pass the handler function you wrote as a callback
asyncFunctionCreateFruits (handleFruits);

If it’s a framework’s API, the documentation will explicitly document the parameter for this callback. Or if you wrote the above function, you’ll need document it.

The magic, or what made it so popular, is that the pattern callback function, besides its achieved asynchronous, it separated the concern from developer that you are actually handling the async result. In the above example, when developer write the handleFruits function, it doesn’t matter to the developer if its passed as a synchronous or asynchronous. The callback function is like a piece of code that’s been executed “just there”. For example, if I copy paste the above callback into the async function and change its name slightly,

function asyncFunctionCreateFruitsAndEatThem () {
  // You will declare some variables
  let apple, banana;
  
  // You will perform some magic
  // Blah, bleb, blahblah...
  // It could takes awhile
  // ...
  // ...

  // Finally, the magic finished,

  // I am a chimpanzee
  console.log("eat", fruit)
  console.log("eat", moreFruit)
}

asyncFunctionCreateFruitsAndEatThem();
4 Likes

Now after the explanation of callbacks, we back to your question (eventually puff~).

The answer is very simple, the done() is a callback function or naming it handler function (because it handle the result). Its named so, by the example, to imitate that you are working in a real server.

In terms the error and data parameters, as documented by the Mongoose (although its documentation is really bad), this save method will receive two parameters in its callback (err, doc).

I’ll roughly immitate, and probably won’t be the truth XD, how Mongoose would impliment this API, just to help you better understand the nature of callbacks APIs.

Document.prototype.save(callback) {
    let err, doc;

    // Blah...Bleb...Blahblah......
    // ...
    
    callback(err, doc)
}

Then when you use this method,

const mongoose = require('mongoose');
mongoose.connect(process.env.MONGO_URI);

const personSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  age: Number,
  favoriteFoods: [String]
});

const Person = mongoose.model('Person', personSchema);

const createAndSavePerson = function(done) {
  const ivan = new Person({
    name: "Ivan",
    age: 22,
    favoriteFoods: ['Pancake', 'Fried egg']
  });
  
  ivan.save((err, data)=> err ? done(err) : done(null, data));
};

So later on in your server, you’d call,

function handleCreateAndSavePerson (err, doc) {
  if (err) throw err;
  // Do something with the returned doc
  console.log(doc)
}

createAndSavePerson(handleCreateAndSavePerson);
13 Likes

Bless you. Have four internet hugs for this great response.

Love you Tyrael, you really are an angel!

Can anyone here help me with the error:

internal/process/warning.js:18 (node:19238) DeprecationWarning: current URL string parser is deprecated, and will be removed in a future version. To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.
writeOut @ internal/process/warning.js:18

//MY CODE//
var express = require(‘express’);
var app = express();
var mongoose = require(‘mongoose’);
var Schema = mongoose.Schema;
mongoose.connect(process.env.MONGO_URI)

var db = mongoose.connection;
db.on(‘error’, console.error.bind(console, ‘connection error:’))
db.once(‘open’, function() {
console.log(‘Connected to MongoDB’)
})

//Create a model
var personSchema = new Schema({
name: {type: String, required: true },
age: Number,
favoriteFoods: [String]
})

var Person = mongoose.model(‘Person’, personSchema);

//Create and save a model
var createAndSavePerson = function(done) {

var person = new Person({name: ‘Priyanka’, age: 22, favoriteFoods: [“bread”, “butter”]})
person.save((err,data)=> (err)? done(err):done(null,data))
};

The deprecation warining is commonly seen when the API is going to remove/change this method in the next version. You can simply ignore it, or use the updated method, or use an older version of the framework in your package.json file.

It’s best to use the same package versions as the FCC course do, to avoid such problems.

If you wish to use the latest packages. As wrote by the Mongoose documentation Mongoose v8.0.2: Connecting to MongoDB

The connect method also accepts an options object which will be passed on to the underlying MongoDB driver.

mongoose.connect(uri, options);

So effectively you call the connect
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true });

1 Like

Thank you so much @Tyrael

Thanks. I had the code right, but did not use ‘Person’ as the name of my model. I thought I could use an actual person’s name. mongoose.model(‘Person’, Model) worked for me.