The project is generated by LoopBack.
We are going to build out an API that will allow CRUD operations for Teams and Players, and query for players that belong to teams. This project is concerned with the back end, and doesn't need to have a front facing web app. The purpose of this workshop is to demonstrate how using a framework can make building and maintaining APIs easy.
Install the Loopback CLI generator.
$ npm install -g loopback-cli
If you try to simply npm install -g strongloop
without following these special instructions you will likely see a ton of errors.
Scaffold out the files for your new app using:
$ lb
Choose the arrow and enter keys to select the following options:
- node-200-loopback-workshop
- node-200-loopback-workshop
- 3.x (current)
- api-server (A LoopBack API server with local User auth)
$ cd node200-loopback-workshop
$ git init
There are already tests setup for you to ensure your project includes all the steps from this workshop. Add the following npm script to your package.json under scripts
in the object, update it to have:
"scripts": {
"test": "mocha test/*.spec.js"
}
Then add the test runner's dependencies with:
$ npm install --save-dev mocha chai chai-http
Loopback has mechanisms to authenticate users, but we will want to disable this so that we will not have to log in (with a username and password) to work with our API. To disable authentication in a Loopback application you can comment out the enableAuth method in server/boot/authentication.js
. Comment out the line below to turn authentication off.
// server.enableAuth();
The main files that have been created include some that you should already be familiar with:
.editorconfig
.eslintignore
.eslintrc
README.md
.gitignore
And then a few that require a brief introduction to usage:
client/README.md
- A simple document that is a placeholder that reminds you where to put your front end app.server/boot/root.js
- this is the first file Loopback will run. It is the entry point if you would like to trace applications code runs. It will boot up the server application.server/middleware.development.json
- this will be used to define/configure the middleware that should be used on each request.server/middleware.json
- this is an alternate file that will be used to configure the middleware when the application is running in production mode. You may want to turn off things like debug logs in production.server/server.js
- this contains the main server logic for the Express app and wires up the routing.server/boot/authentication.js
- this contains the authentication mechanisms, and can help secure access to endpoints.
You can run your application using:
$ node .
Then in your browser navigate to: http://localhost:3000
You will see a status message telling you the uptime of the application. Next, checkout the Swagger interface here: http://localhost:3000/explorer
The Loopback Explorer has already created a User
model, and provided an interface that documents the api, and allows you to do some CRUD operations.
Currently, the application is using an in-memory database. Please note that every time you stop and start the app you will lose all the data that has been posted. Where Loopback really shines is in its ability to quickly connect to different datasources, so that you can swap out the storage mechanism, without having to change your code. You will find more detailed information about it in the DataSources
section of this document.
In the Loopback Explorer Click on User, and then click POST (/Users), then using the form in the UI add a new user as a JSON object in the value field:
{
"realm": "example",
"username": "mike",
"email": "mike@example.com",
"emailVerified": true
}
You don't need to add an id
key/value, one will be created for you. You can also see an example by toggling the Model/Example link (under the Data Type column).
Next, click the Try it out!
button. You should get a success message if you have entered a valid object (it must conform to the schema). If not check the response codes and error message(s) to fix your errors.
Then, once you have successfully added a user, click on the GET (/Users), and Try it out!
to see if you can get all users. NOTE: if you did not turn off authentication you may get an error. See Authentication above.
Loopback has added this model based on the choices made at the CLI. Let's use the CLI to start building out the remaining data model we need for our API.
Models are used to define data objects for our API to store and retrieve. Here, we'll be adding two models: player
and team
.
Add a model using
$ lb model
...with the following options:
- db (memory)
- PersistedModel
- Yes
- Press Enter and Accept Default
- Common
Then, enter the properties and types as follows:
Note: they do not need to be required or have default values
name: string
position: string
average: number
When you have added the last one hit enter at the name prompt and it will exit out of the CLI.
Use that same process to add a new model called team
with the following schema:
name: string
city: string
market: number
Then, return to the Loopback Explorer and check out the new API endpoints you have created and have documented.
Using the POST method in the Loopback Explorer create the following 3 teams:
{ name: "Braves", city: "Boston", market: 4000000 },
{ name: "Padres", city: "San Diego", market: 2000000 },
{ name: "Lakers", city: "Minnesota", market: 800000 }
There are many types of relationships provided by Loopback. The most common is a one-to-many, which loopback calls a HasMany relationship. It is described this way, but it is also attainable by using BelongsTo or defining the many-to-one side. Review the documentation here to see more examples.
Let's add a relation between the teams and payers by using the following command in the CLI:
$ lb relation
team
- has many
player
- Press Enter to accept the default players
- Press Enter to accept none
- No Enter through the remaining options to accept all defaults
Note: Through models are useful when you have a many-to-many relationships. Loopback has a HasManyThrough relation type that makes it simple to achieve many-to-many and expose the correct endpoints auto-magically.
Next, using the Loopback Explorer review the new API endpoints. You should now see that you can add players through a team
endpoint http://localhost:3000/explorer/#!/team/team_prototype_create_players
Also look at the POST (/player) and note that the schema changed. Without any intervention on our end the Loopback CLI has added a teamId
field for us. WOW!
Using the explorer use the team
id to insert a few players onto any of the three teams:
name: "Larry Boggs",
position: "P",
average: 2.50
name: "Wade Rockerson",
position: "C",
average: 2.30
name: "Daryl Blueberry",
position: "LF",
average: 3.30
Now, use the GET (GET /teams/{id}/players) to query for all the players on one of the teams.
Next, try deleting a team
that has players, what happens?
The methods that Loopback creates for our CRUD operations are called Remote Methods
, and you can choose to add additional handlers that are called when a model is accessed. You can also remove them from display/usage on the Explorer.
If you look in your /common/models
folder you will find that there is a .json
file and a .js
file for each model. The configuration is held as JSON, and the Javascript file is where you may write code that will execute when the model is interacted with. It's common to want to either manually add other types of operations (besides the CRUD operations) as endpoints. Or, there may also be a need to hook into an existing operation or trigger some other custom handlers to fire off via your API.
The CLI does a good job of providing much of what is needed, however here are a couple of scenario's where remote methods would come in handy:
- if a new
player
is created, we needed to send an SMS message to the coach - if we needed the ability to use a custom endpoint
/active
to GET a count of all active players
In each case we would be needed to handle the business logic desired, and possibly generate a couple new endpoints, instead of relying upon only the ones generated by Loopback CLI. Review the Loopback docs here for more info here about Registering a Remote Method before moving on to the next section.
You can remove Remote Methods inside each Model's /common/models/
Javascript file using the following syntax:
// inside the code block that is being exported
{MODEL_NAME}.disableRemoteMethod("delete", true);
That would remove the ability to use the DELETE method on that model.
Quickly review the docs here for MongoDB connector and then let's add MongoDB as a datasource so that our application will persist data to a database. At the command line let's start adding a new datasource using:
$ lb datasource
- MongoDB
- MongoDB (supported by StrongLoop)
Enter through the remaining options to accept all defaults
Note: The last thing Loopback did for us was install the loopback-connector-mongodb package from npm (this is the system Loopback will use as a "plugin" and translate between Looback and the database driver)
Finally, we will need to update our models to use our new connector name: MongoDB
in the model-config.json
. Note that this is needed because we originally chose the option of in memory. Update it to replace db
with MongoDB
to match the new datasource.
"player": {
"dataSource": "MongoDB",
"public": true
},
"team": {
"dataSource": "MongoDB",
"public": true
}
Note: Once this is setup, be sure you have an instance of MongoDB running when your app starts or it will crash.
Now you should be able to stop and start the server, and the data should persist. Give it a try.
Another way to test it out is to add a new team
using the Loopback Explorer, and then do a GET (/teams) look a the id
Loopback creates by default. It should look like a MongoDB ObjectID ie. 59b9a3aa474ecd50f081578f
By default we have mainly been assuming that development and production modes are the same, however in the real world, you may have separate databases, or configurations used when you run your app in production vs. in development. Another neat feature of Loopback is that it assumes file names will follow patterns. So for example if you want to setup a configuration to only apply when the environment is development you could add the contents of the JSON file to config.development.js
like this:
module.exports = {
"restApiRoot": "/api",
"host": "0.0.0.0",
"port": 3000
}
Loopback will look for this file, and use it (if it detects you are running in the default development mode). Then you could dynamically set the port, or host values like this:
{
"restApiRoot": "/api",
"host": process.env.HOST,
"port": process.env.PORT
}
This will come in really handy if you deploy your application and need to set the port and host name dynamically. Be cautious, you may still need to keep a config.json file around, even though the settings will be overridden by either a config.development.js
or config.production.js
.
Notice that Loopback created a file for us called datasources.json
. If you open the file, there should be two datasources listed, one for in memory storage called db
and one that you created called MongoDB
. This will work fine locally, but how about if you want to deploy your app to production?
You will need to create two files in the /server
directory: datasources.local.js
and datasources.production.js
.
Move the contents of your datasources.json
file to your datasources.local.js
file and export them like this:
module.exports = {
"db": {
"name": "db",
"connector": "memory"
},
"MongoDB": {
"url": "mongodb://localhost:27017",
"name": "MongoDB",
"connector": "mongodb"
}
}
Your datasources.json
file should contain an empty object like this:
{}
You can leave your datasources.production.js
file empty for now, we will need it when we deploy our applicaton to Heroku later in the lesson.
Note: you still need to keep a datasources.json file around, even though the settings will be overridden by either a datasources.local.js
or datasources.production.js
.
Loopback makes it easy to add middleware without touching code. You add middleware modules in a two step process. First you need to install the package using npm, then you configure the middleware in a json object in server/middleware.json
.
Install Morgan using npm install --save-dev morgan
Add the following to server/middleware.json
in the initial
section of the JSON structure.
"morgan": {
"params": ["dev", { "buffer": true } ]
}
It is common to serve both your website and an API on the same server. Rather than have to make any special routes or use templates, the fastest way to do this with Loopback is to add middleware to serve up static content.
This does not even require adding a package, it simply requires setting up a configuration for middleware, and Loopback will wire up Express serve static for us.
Update the files
part of the JSON structure in server/middleware.json
to have the following value:
"files": {
"loopback#static": {
"params": "$!../client"
}
}
The $!..
in this case, is Loopback syntax to map the root folder of the project. Now any files that you add to the client folder, will be served at the /
path of your server.
Add a simple index.html
page to the client
folder so something will be displayed when users navigate to the base url.
Make sure MongoDB is running before you run the tests, but you do not need to run the server. Run the tests using:
$ npm run test
Using what you have learned in previous lessons:
- Upload your project to Github.
- Sync your project with CircleCI, so that every time you push to the master branch, CircleCI will run your tests.
- When the CircleCI tests are passing, it should automatically push a build to your Heroku application.
- Make sure you set your mongoDB enviroment variables inside of the settings. Open CirlceCI, and go to Settings in top right corner. Select Envirmonent Variables. Add new enviroment variable pointing to your mongdb_url in production.
Here are some helpful hints for deploying a Loopback app to Heroku with mongoDB:
-
You will need to create a configuration variable in Heroku to let Loopback know that it's running in a production environment. The Heroku dashboard makes it easy to add and remove config vars. Find your config vars and add this one:
ENV=production
. -
Since we are using mongoDB as our database for this application, we need to have an instance of mongoDB running on our production server. We can accomplish this using the Heroku add-on for mLab. Find your add-ons through the Heroku dashboard, and add mLab. Make sure you provision a free (sandbox) plan.
Earlier in the lesson, you created a datasources.production.js
file. Open it up and insert the following code:
module.exports = {
"MongoDB": {
"name": "MongoDB",
"connector": "mongodb",
"url": process.env.MONGODB_URI
}
}
Note: the MONGODB_URI
config variable was created auto-magically when we added mLab. Go look at your config vars in Heroku to see the magic!
Once you have made this change, push your project to the Github master branch, let CircleCI run your tests, and then it should automatically deploy to Heroku. Check out your Loopback API explorer at your-app-name.herokuapp.com/explorer.
Once all your tests are passing, and you have deployed your site to Heroku, you can Submit your project
-
Add a simple page using React or EJS templates to display the players and teams
-
Write some tests for the back end
-
Add a remote method that hooks into the players model (use a remote method) and log a message when a new
player
is added to ateam