Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save michaelerobertsjr/6972a60ba8ef4fe4b255227ccd1f1420 to your computer and use it in GitHub Desktop.
Save michaelerobertsjr/6972a60ba8ef4fe4b255227ccd1f1420 to your computer and use it in GitHub Desktop.
workshop
# Loopback Workshop
---
## Overview
---
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.
## Prerequisites
---
Install the Loopback CLI generator.
### OS X and Linux
```bash
$ npm install -g loopback-cli
```
### Windows
[Follow these instructions](https://loopback.io/doc/en/lb3/Installing-on-Windows.html)
If you try to simply `npm install -g strongloop` without following these special instructions you will likely see a ton of errors.
## Getting Started
---
Scaffold out the files for your new app using:
```bash
$ lb
```
Choose the arrow and enter keys to select the following options:
#### What's the name of your application?
* node-200-loopback-workshop
#### Enter name of the directory to contain the project:
* node-200-loopback-workshop
#### Which version of LoopBack would you like to use?
* 3.x (current)
#### What kind of application do you have in mind?
* api-server (A LoopBack API server with local User auth)
## Initialize your repository
```
$ cd node200-loopback-workshop
$ git init
```
## Add Test Script
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:
```javascript
"scripts": {
"test": "mocha test/*.spec.js"
}
```
Then add the test runner's dependencies with:
```bash
$ npm install --save-dev mocha chai chai-http
```
## Authentication
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.
```javascript
// server.enableAuth();
```
## Summary of Files Created
---
The main files that have been created include some that you should already be familiar with:
```javascript
.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.
## Progress Check
You can run your application using:
```bash
$ node .
```
Then in your browser navigate to: [http://localhost:3000](http://localhost:3000)
You will see a status message telling you the uptime of the application. Next, checkout the [Swagger](https://swagger.io/) interface here: [http://localhost:3000/explorer](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.
## Friendly Warning
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.
## Add a user
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:
```javascript
{
"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.
## Adding Models
---
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
```bash
$ lb model
```
...with the following options:
#### Select the data-source to attach `player` to:
* db (memory)
#### Select model's base class
* PersistedModel
#### Expose `player` via the REST API
* Yes
#### Custom plural form
* Press Enter and Accept Default
#### Common model or server only
* 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.
## Insert Teams using the Explorer
---
Using the POST method in the Loopback Explorer create the following 3 teams:
```json
{ name: "Braves", city: "Boston", market: 4000000 },
{ name: "Padres", city: "San Diego", market: 2000000 },
{ name: "Lakers", city: "Minnesota", market: 800000 }
```
## Create Relationships
---
There are many types of relationships provided by Loopback. The most common is a one-to-many, which loopback calls a [HasMany](https://loopback.io/doc/en/lb3/HasMany-relations.html) relationship. It is described this way, but it is also attainable by using [BelongsTo](https://loopback.io/doc/en/lb3/BelongsTo-relations.html) or defining the many-to-one side. Review the documentation [here](https://loopback.io/doc/en/lb3/Creating-model-relations.html) to see more examples.
Let's add a relation between the teams and payers by using the following command in the CLI:
```bash
$ lb relation
```
#### Select the model to create the relationship from:
* `team`
#### Relation type:
* has many
#### Choose a model to create a relationship with:
* `player`
#### Enter the property name for the relation:
* Press Enter to accept the default players
### Optionally enter a custom foreign key:
* Press Enter to accept none
### Require a through model?
* 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](https://loopback.io/doc/en/lb3/HasManyThrough-relations.html) 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](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!
## Insert Players
---
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?
## Remote Methods
---
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.
### Registering remote Methods
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](https://loopback.io/doc/en/lb3/Remote-methods.html#registering-a-remote-method) before moving on to the next section.
### Removing remote Methods
You can remove Remote Methods inside each Model's `/common/models/` Javascript file using the following syntax:
```javascript
// 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.
## DataSources
---
Quickly review the docs here for [MongoDB connector](http://loopback.io/doc/en/lb3/MongoDB-connector.html) 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:
```bash
$ lb datasource
```
#### Enter the data-source name:
* MongoDB
#### Select the connector for MongoDB:
* MongoDB (supported by StrongLoop)
#### All the other options
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.
```javascript
"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
## Configuring
---
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:
```javascript
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:
```javascript
{
"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`.
### Configure DataSources
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:
```javascript
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:
```javascript
{}
```
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`.
## Middleware
---
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`.
### Add Morgan
Install Morgan using `npm install --save-dev morgan`
Add the following to `server/middleware.json` in the `initial` section of the JSON structure.
```javascript
"morgan": {
"params": ["dev", { "buffer": true } ]
}
```
## Serve Static
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:
```javascript
"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.
## Testing the project
Make sure MongoDB is running before you run the tests, but you do not need to run the server. Run the tests using:
```bash
$ npm run test
```
## Deploy your app to Heroku
Using what you have learned in previous lessons:
1. Upload your project to Github.
2. Sync your project with CircleCI, so that every time you push to the master branch, CircleCI will run your tests.
3. When the CircleCI tests are passing, it should automatically push a build to your Heroku application.
4. 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](https://devcenter.heroku.com/articles/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](https://www.mlab.com). 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:
```javascript
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.
## Submit your work
Once all your tests are passing, and you have deployed your site to Heroku, you can submit the url to your project and repository to an instructor.
## Bonus
* 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 a `team`
##
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment