Lesson 13

Building a Node.js backend

7

⚠️ This tutorial uses an old version of Ember. Some things might have changed!

In this lesson, we'll finally get rid of our Fixture Adapters and instead build a real REST API with a database connection.


The most important thing to know is that Ember.js is completely backend-agnostic. You could write this part just as well in Ruby, PHP, Python or any other server language. In this lesson though we'll use Node.js since we're already familiar with the quirks of JavaScript.


Please keep in mind that this course isn't about Node.js, so we might not explain every step as elaborately as in the previous chapters. What you should know is that, just like Ember has opinions about your file structure for example, Ember Data is also very opinionated when it comes to how your REST API's URLs should be, and what the JSON they return should look like.


Although it might seem annoying at first to structure your API responses according to a framework's particular opinions, the conventions are usually well-grounded and optimized for efficiency. If you, for some reason, in some future project, really don't like certain conventions set by the REST adapter, you can customize your adapter, or even ignore Ember Data and just use normal JSON.

Creating a November project



In order to be up and running as quickly as possible, we'll use a command line tool called November CLI to generate our Ember-friendly Node.js API (full disclosure: I am the author of the library).


Open a new terminal window and make sure this time that you are outside of your Ember folder. We want the API to be a standalone project with its own file structure.


It is good practice to keep your frontend and backend parts separate. The backend's sole purpose should be to deliver the necessary data when needed, and our Ember project is just one way to present that data (as a web app). At a later stage, we might want to use the same backend to power a mobile app for example.


First, install November CLI globally on your computer:

When that's done, you can create a new November project. Notice that the commands are very similar to the ones of Ember CLI.

After it is done installing all the dependencies, cd into the directory and start the server with npm start. By the way, I recommend you also install nodemon globally (if you haven't already) and run the project with the nodemon command instead at a later stage. That way, you won't have to restart the server whenever a file changes, which is quite handy.


By default, the server will run on http://localhost:9000


If you see this, then you know it works well!


Configurations

Before we go on, let's also set up our database connection. If you haven't installed MySQL, I suggest you do so with homebrew (if you have MAMP installed, you could also use that).

After that, make sure you have MySQL running by running mysql.server start in a terminal window.


Next, we create our database:

Now that the database exists, we have to make sure that that's the one we use in our November project:

config/config.json

Creating the user model

Now that the configuration is done, let's create our first table which will store all of our app's users! Make sure you are in the November directory and run:

You'll see that November creates some files for us. Let's open up the model-file so that we can define the user attributes that we want to store in our database (these will be similar to the ones we have in our Ember project):


All we need to define for now are columns for [code]username[/code] and about_me.


Sequelize will by default add columns called created_at and updated_at which are populated and updated automatically. The only thing we need to specify is that we want our created_at-column to be called joined_at instead (since that makes more sense for a user).

app/models/user.js

That's all you need to do! Now visit http://localhost:9000/users, and if you see this JSON response...

...then that means that your users-table was successfully created and that the REST URLs are working!


By the way, in order to easily read JSON in your browser, I recommend that you install the JSON Formatter Chrome plugin.

Creating the chirp model

The next step is to create a table for our chirps.

We'll keep the model very simple here as well, but note that we're specifying a relationship to the user table (Sequelize will automatically create a foreign key). The onDelete: 'cascade'-part says that, if the user is deleted, then all of its chirps will be deleted as well.

app/models/chirp.js

We also need to acknowledge this relationship from the user model, so go back to the user-file and add a classMethod to it:

app/models/user.js

Now visit http://localhost:9000/chirps, and you should see an empty array, this time under the key chirps, which proves that the table was created successfully!



Defining the followers and followees

Finally, we need a special join-table in order to track who our users are following.

Now, this model is a little special since it doesn't really have any attributes of its own, it only stores two foreign keys that are both linked to the user table: the follower and the followee.


Because of this, we won't need to change anything in the follow model-file. Instead, we'll go to the user's model file, and add these two associations (right after the chirp-association):

app/models/user.js

Again, visit http://localhost:9000/users and your follow-table will be created.

Verify your tables

These are the only tables that we need! To make sure that they've all been created correctly, you can launch the mysql command line tool and type in some SQL commands to verify that they're all there:

If you get the same results, you're all set and ready to convert the data fixtures in your Ember app to use the Node.js backend!


If you, on the other hand, get results that differ from the ones above, delete the faulty table and please go through this lesson again. You probably made a mistake somewhere, and it's important that you have the exact same table structure.


The upcoming chapters will rely heavily on a working database, so make sure you get it right!

Comments

Daniel Jeffery

In the follows table, both followee_id and follower_id have "PRI" next to them, is this indicating that together they are the primary key?


I believe this is called a 'composite key', and means that the table is ensuring that the two columns together are unique?


I think I am confirming my own question here, but would be interesting to have another perspective!

Tristan Edwards

@danieljeffery: That's exactly right! As you probably noticed, the follow-table doesn't have an id-field. By making a composite key out of the follower and followee ids, we can guarantee that there won't be any duplicates on the database level.

Adam Marinelli

What are our options for Windows? http://dev.mysql.com/downloads/windows/ ?

Malik Dixon

Hey Tristan. You mentioned that you can use MAMP in place of MySql. Can you explain it or send a link. Thanks.

Tristan Edwards

@adammarinelli: That's it! You need to download the MySQL server, and then start it like this.

Tristan Edwards

@malikdixon47: If you're on a Mac, you can download MAMP, open the program and click "Start servers". It's the equivalent of running mysql.server start.

Adam Marinelli

Might be worth noting this is in a different directory then our Ember app.

Kirk Israel

(Sorry for being so talkative) You might want to mention that, once they have the js set correctly, they can drop tables and reload users and chirps and things should be made correctly

Kirk Israel

where did the table follows come from? Is it baked into November or did November somehow figure it out from the js? (I don't see it in there, is the thing)

profile/avatar/default
Gwynneth Davidoff

Under username, mine has the key "UNI" but I think that's actually correct, since we set "unique: true" under username in the user model. Please advise if this is still wrong.

Daniel Jeffery

Hey Kirk, it creates a file called app/models/follow.js on the November side of things which is what creates the table.


The part sequelize.define('follows'is what creates the table and the end point (I think this is how it works).


That model file should definitely have been auto generated though!

Kirk Israel

what's the difference between npm start and node server.js ?

profile/avatar/default
Normlorenz

Any idea how to get the api to run on port 8081 in C9? C9 supports multiple ports per workspace but both the app and the api seem to default to port 8080.

Adam Marinelli

I justed used nodemon server.js to start November so my initial thought is that they are doing the same thing.

Levi Nelson

I kept getting an error until I did<strong class="userinput" style="color: rgb(0, 0, 0); font-size: 13px; line-height: 20px; background-color: transparent; margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: baseline;"><code style="margin: 0px; padding: 1px; border: 0px; outline: 0px; font-size: 12px; vertical-align: baseline; background-color: inherit; color: inherit; font-family: 'Courier New', Courier, fixed, monospace;">mysql --user=root mysql</code></strong> to start the MySQL command line tool.

Malik Dixon

I am getting access denied errors while creating the database. I can use some direction. Thank you.

Tristan Edwards

@malikdixon47: Make sure that you're logged in as a root user: mysql -u root -p. It might also be a privilege issue, check out this answer: http://askubuntu.com/questions/461064/unable-to-create-database-due-access-denied

Adam Marinelli

Try adding -p at the end to get the password prompt: http://stackoverflow.com/a/9040798/2457991

Alan Rad

Hi Tristan. If possible, Could you please recommend a reliable JSON API adapter for Ember Data that works well with the standard ASP.NET REST API. Thanks!

Tristan Edwards

@alanrad: Hi Alan! I'm not too familiar with ASP.NET development so unfortunately I don't know what their standard REST API looks like. I did find a solution the other way around though, a JSONAPI implementation for ASP: https://jsonapi.codeplex.com Maybe that helps?

Alan Rad

@tristan Hi Tristan, thanks for your message. But we didn't want to change a back-end APIs so we could re-use them for future apps with different frameworks. We ended up writing our own application serializer and adapter and it works like a charm. Thanks for this great course! :)

Andy Pickler

After foreignKey: 'user_id' we currently have allowNull: false

Daniel Jeffery

Bump!

Daniel Jeffery

Looks like allowNull: false has been added :)

Daniel Jeffery

I see what you are saying - allowNull: false is omitted - from what I can see that looks like an oversight and should be there!

Kirk Israel

Agreed, please fix or remove.. in general this part of the code is very hard to follow along with since the syntax is unfamiliar possibly on two levels! (November and ES6-ish)

Kirk Israel

Well, you have to restart the node server... I know you mentioned nodemon before maybe you should reiterate that here

Daniel Jeffery

nodemon all the way!

Chris Freeman

It appears that the option to rename `createdAt` is currently broken in sequelize: https://github.com/sequelize/sequelize/issues/4103 As a result, it doesn't seem that there's any way to actually rename the column to joined_at without actually altering the schema in mysql. Not sure how that affects things on the Node side, however...

Lynn Alexander

getting:ERR_CONNECTION_REFUSED at http://localhost:9000/users....on windows machine.... any suggestions?

Lynn Alexander

this is what I get now: {"error":{"code":500,"title":"Could not load users"}}

Daniel Jeffery

@chrisfreeman: I didn't have the issue creating the column joined_at. Did you resolve that issue?

Daniel Lourenço

@lynnalexander: I'm getting the same issue on a Mac.

Tristan Edwards

@danielloureno: Could it be that you forgot to start the MySQL server? Make sure you run mysql.server start

Lynn Alexander

server is running and still getting: {"error":{"code":500,"title":"Could not load users"}}

Andreas Karseras

I had the same problem. To fix, I had to enter the MySQL password (surrounded by quotes), to the config.jason save and restart the november server.

Benjamin Keller

Same problem. Added password via mysqladmin and added that password to the config.json file. Still getting the <p class="p1">Error: connect ECONNREFUSED</p>

profile/avatar/default
Accounts

I had this problem too, the issue in my case was that the database was not created for whatever reason with the command line. I checked with Sequel Pro and just added the database there, problem solved.

Daniel Jeffery

On a mac I logged in as the root user ... /usr/local/mysql/bin/mysql -u root


Not sure if that will work for anyone but that worked for me!

Bill Pullen

A little annoying this section is not OS agnostic

Tristan Edwards

@billpullen: You're right, that's bad from my part. Will look into it asap.

Bill Pullen

@tristan looks like its pretty easy to get november to run on windows with a small change. https://github.com/t4t5/november-cli/issues/2 of removing .lookup(). Was wondering if you were interested mb could start project to have vagrant for the book

Tristan Edwards

@billpullen: Merged the pull request. Thanks! Haven't had a Windows machine to test on yet, but I hope it all works. :)

Alid Lorenzo Castano

I am not able to get relationships to work. I followed everything step by step, except that instead of the user to chirp relationship, I have a notebook to note relationship. Without any classMethod, everything works fine. But then when I try to add one, I get error: column &quot;notebook_id&quot; does not exist.

Alid Lorenzo Castano

update: my problem was that I had made changes in my november project but the changes had not updated in my database. As described in &quot;adding authentication&quot; section of the tutorial, you can force the database to update when you do file changes by temporarily changing app/middleware/sequelize.js and setting db.sequelize.sync({force:true}) on line 4.

profile/avatar/default
Tim

Later it is expected that user_id has a NOT NULL restraint on it, but none is specified here.

Daniel Jeffery

Looks like this comment should be about the next section!