What does RESTful API really mean?

REST stands for Representational State Transfer. It is an architecture that allows client-server communication through a uniform interface. It is also stateless, cache-able and has a property called idempotence (for most of its verbs), which means that multiple identical requests have the same cumulated side effects as a single request would.

HTTP RESTful APIs are composed of:

HTTP methods, e.g. GET, PUT, DELETE, PATCH, POST, …

Base URI, e.g. http://localhost:3000

URL path, e.g /todos/2/

Media type, e.g. html, JSON, XML …

In this article I will show you how to use LoopBack to create a RESTful API. LoopBack is a highly extensible, open-source Node.js framework. For more details on our Node.js experience, you can browse our technology stack. Not a Node.js kind of dev? Then check out our articles on converting between web frameworks and programming languages (part 1 and part 2 – data). With Node.js and LoopBack, you can quickly create dynamic end-to-end REST APIs. That being said, the following example is for a task management (TODO) application.

Setup

Understanding the MEAN Stack

Recent years have seen a shift from classical web applications to a Back-End and Front-End segregation, in modern one-page applications. This makes it easier for servers to communicate not only with a web browser, but also with a variety of mobile devices, without having to change one single line of code. One of the most popular approaches is MEAN, a JavaScript stack for building web sites and web applications. MEAN comprises MongoDB, Express, AngularJS and NodeJS.

Throughout this article, I will use three of MEAN’s four components: MongoDb, Node and Express. Node and Express are the foundations upon which LoopBack is built and are bundled here as well. I won’t talk about Angular, but if you are interested, be sure to check out our articles on the topic. You might be interested in validations, charts or complex animations.

NodeJS

Although NodeJS is a JavaScript-based programming language, it runs outside the browser. In fact, we will run our NodeJS code on the server.

To install it, go to NodeJS Website and follow the instructions. Or, alternatively, if you are using Mac and brew, run brew install nodejs in the console. Ubuntu users can use nvm to install NodeJS. Either way, if the installation was successful, you can check Node’s and NPM’s installed versions like this:

node -v # => v0.10.43 npm -v # => 3.8.5 1 2 3 4 5 node - v # => v0.10.43 npm - v # => 3.8.5

Make sure you treat any errors and warnings before continuing.

MongoDB

There is a large variety of data storage options available to every programmer. However, in this article I have chosen to use MongoDB, in order to stay true to the MEAN stack.

MongoDB is a document-oriented NoSQL database, so we could say it is Big Data-ready. Mongo stores data in a JSON-like format and allows the user to perform SQL-like queries against it.

You can install MongoDB following the instructions here. If you have a Mac and brew, simply run: brew install mongodb && mongod , while in Ubuntu the command is sudo apt-get -y install mongodb .

Don’t forget to check version, in order to make sure everything is all right:

mongod --version # => db version v2.6.11 1 2 mongod -- version # => db version v2.6.11

LoopBack

LoopBack is a highly-extensible, open-source Node.js framework. IBM and the StrongLoop team are committed to maintaining and improving LoopBack as an open-source project. As they put it:

Building on LoopBack’s success as an open-source Node.js framework, IBM API Connect provides the newest tools to use with LoopBack projects. It includes a graphical tool with many of the API composition features of StrongLoop Arc, plus assembly and testing of API Gateway policies using the local Micro Gateway. Some features are still available only in Arc but these will be added to API Connect in the next few months.

API Connect also provides its own command-line tool, integrated with API management and gateway features. A free version of API Connect called API Connect Essentials is available for developers to install, free of charge.

Install it using npm:

npm install -g strongloop 1 npm install - g strongloop

Notice the -g option. It means your strongloop installation will be global. Strongloop will also be added in the PATH, so you can run it from anywhere.

Let’s check version:

slc -v # => strongloop v6.0.0 (node v0.10.43) 1 2 slc - v # => strongloop v6.0.0 (node v0.10.43)

For the examples I run in this article, note that I use the version above. The MEAN world is highly dynamic and new versions get cleared and released frequently, so you might have a different one. Please remember that, if reproducing any steps below gives you trouble, it might be a problem of incompatibility between my versions and yours. I recommend you to mimic my library versions for this tutorial, or at least check if your versions are compatible.

Creating the project

If you followed the previous steps, you should have all you need to complete this tutorial. Basically, we are going to build an API that allows users to CRUD (Create-Read-Update-Delete) Todo tasks from the database.

Before starting the project, I need to run MongoDB.

mongod 1 mongod

Now that the database is running, I can start creating a RESTful server with LoopBack.

Run the following to create the project:

mkdir Todos && cd Todos slc loopback ? What's the name of your application? (Todos) 1 2 3 mkdir Todos && cd Todos slc loopback ? What ' s the name of your application ? ( Todos )

Here I leave everything blank, so the project is generated in the current folder, with the name “Todos”. Now I will select what kind of project I need:

? What kind of application do you have in mind? (Use arrow keys) ❯ api-server (A LoopBack API server with local User auth) empty-server (An empty LoopBack API, without any configured models or datasources) hello-world (A project containing a basic working example, including a memory database) 1 2 3 4 ? What kind of application do you have in mind ? ( Use arrow keys ) ❯ api - server ( A LoopBack API server with local User auth ) empty - server ( An empty LoopBack API , without any configured models or datasources ) hello - world ( A project containing a basic working example , including a memory database )

After the project is created, it’s time to start server:

node . # => Web server listening at: http://0.0.0.0:3000 # => Browse your REST API at http://0.0.0.0:3000/explorer 1 2 3 node . # => Web server listening at: http://0.0.0.0:3000 # => Browse your REST API at http://0.0.0.0:3000/explorer

LoopBack created a user model for the API and all the basic routes needed. If you open a browser and access http://0.0.0.0:3000/explorer , you will see something like this:

The explorer entries are proof of LoopBack creating an API for User objects. Later you will see in more depth how the explorer works. If you add new models in the LoopBack project, the corresponding routes will be created automatically. The explorer interface makes manual testing straightforward, so use it wisely. After I will set up my database and add all the models, I will use this explorer to show you how it works.

MongoDB Connection

LoopBack models connect to backend systems such as databases via data sources that provide create, retrieve, update, and delete (CRUD) functions. Data sources are backed by connectors that implement the data exchange logic using database drivers or other client APIs. In general, applications don’t use connectors directly, rather they go through data sources using the DataSource and PersistedModel APIs.

LoopBack provides connectors for most popular relational and NoSQL databases. These connectors implement create, retrieve, update, and delete operations as a common set of methods of PersistedModel. When you attach a model to a data source backed by one of the database connectors, the model automatically acquires the create, retrieve, update, and delete methods from PersistedModel. The data access methods on a persisted model are exposed to REST by default. You can connect models using relations to reflect relationships among data.

Here you can see LoopBack create a RESTful server with all routes for user model. I add a data source and new models, by first stopping the server and then running the following commands:

slc loopback:datasource ? Enter the data-source name: mongoDB 1 2 slc loopback : datasource ? Enter the data - source name : mongoDB

After I set the name for my data source, I will select a connector. Let’s pick MongoDB connector:

? Select the connector for mongoDB: In-memory db (supported by StrongLoop) IBM DB2 (supported by StrongLoop) IBM Cloudant DB (supported by StrongLoop) ❯ MongoDB (supported by StrongLoop) MySQL (supported by StrongLoop) PostgreSQL (supported by StrongLoop) Oracle (supported by StrongLoop) (Move up and down to reveal more choices) 1 2 3 4 5 6 7 8 9 ? Select the connector for mongoDB : In - memory db ( supported by StrongLoop ) IBM DB2 ( supported by StrongLoop ) IBM Cloudant DB ( supported by StrongLoop ) ❯ MongoDB ( supported by StrongLoop ) MySQL ( supported by StrongLoop ) PostgreSQL ( supported by StrongLoop ) Oracle ( supported by StrongLoop ) ( Move up and down to reveal more choices )

MongoDB connector and all LoopBack connectors will need to be configured after you select them.

Connector-specific configuration: ? host: localhost ? port: 27017 ? user: ? password: ? database: todosDB ? Install loopback-connector-mongodb@^1.4 (Y/n) Y 1 2 3 4 5 6 7 Connector - specific configuration : ? host : localhost ? port : 27017 ? user : ? password : ? database : todosDB ? Install loopback - connector - mongodb @ ^ 1.4 ( Y / n ) Y

Create Models

Remember our “Working with data” article? A LoopBack model represents data in backend systems such as databases, and by default has both Node and REST APIs. Additionally, you can add functionality such as validation rules and business logic to models.

Every LoopBack application has a set of predefined built-in models such as User, Role, and Application. You can extend built-in models to suit your application’s needs.

After we hook up the project database (with MongoDB), it is time to create models. This operation is very simple, just like the ones you’ve done so far:

slc loopback:model ? Enter the model name: todos ? Select the data-source to attach todos to: db (memory) ❯ mongoDB (mongodb) (no data-source) 1 2 3 4 5 6 slc loopback : model ? Enter the model name : todos ? Select the data - source to attach todos to : db ( memory ) ❯ mongoDB ( mongodb ) ( no data - source )

? Select the data-source to attach todos to: mongoDB (mongodb) ? Select model's base class Model ❯ PersistedModel ACL AccessToken Application Change Checkpoint (Move up and down to reveal more choices) 1 2 3 4 5 6 7 8 9 10 ? Select the data - source to attach todos to : mongoDB ( mongodb ) ? Select model ' s base class Model ❯ PersistedModel ACL AccessToken Application Change Checkpoint ( Move up and down to reveal more choices )

? Select model's base class PersistedModel ? Expose todos via the REST API? (Y/n) Y 1 2 ? Select model ' s base class PersistedModel ? Expose todos via the REST API ? ( Y / n ) Y

? Expose todos via the REST API? Yes ? Custom plural form (used to build REST URL): 1 2 ? Expose todos via the REST API ? Yes ? Custom plural form ( used to build REST URL ) :

? Common model or server only? common ❯ server 1 2 3 ? Common model or server only ? common ❯ server

After you create the todos model, you need to add properties:

Let's add some todos properties now. Enter an empty property name when done. ? Property name: name 1 2 3 4 Let ' s add some todos properties now . Enter an empty property name when done . ? Property name : name

Named the first property, then set property type:

? Property name: name invoke loopback:property ? Property type: (Use arrow keys) ❯ string number boolean object array date buffer (Move up and down to reveal more choices) 1 2 3 4 5 6 7 8 9 10 11 ? Property name : name invoke loopback : property ? Property type : ( Use arrow keys ) ❯ string number boolean object array date buffer ( Move up and down to reveal more choices )

You also need to set a flag representing if the property is required or not. Leave the answer blank to stick with defaults (non-required):

? Property type: string ? Required? (y/N) 1 2 ? Property type : string ? Required ? ( y / N )

Last you need to set a default value, if needed. I don’t want a default value here, so I leave this line blank:

? Required? No ? Default value[leave blank for none]: 1 2 ? Required ? No ? Default value [ leave blank for none ] :

After all these steps, I added in the todos model a property with name: name, type: string, required: No, default value: none. For the next three properties, repeat the required steps and set them as such:

? Property name: completed invoke loopback:property ? Property type: boolean ? Required? No ? Default value[leave blank for none]:false ? Property name: note invoke loopback:property ? Property type: string ? Required? No ? Default value[leave blank for none]: ? Property name: updated_at invoke loopback:property ? Property type: date ? Required? No ? Default value[leave blank for none]: now 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ? Property name : completed invoke loopback : property ? Property type : boolean ? Required ? No ? Default value [ leave blank for none ] : false ? Property name : note invoke loopback : property ? Property type : string ? Required ? No ? Default value [ leave blank for none ] : ? Property name : updated_at invoke loopback : property ? Property type : date ? Required ? No ? Default value [ leave blank for none ] : now

To quit the property setter you just need to leave the Property name empty and press Enter:

Enter an empty property name when done. ? Property name: 1 2 Enter an empty property name when done . ? Property name :

After all these properties are set, the todos model will look like this:

{ "name": "todos", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "properties": { "name": { "type": "string" }, "completed": { "type": "boolean", "default": false }, "note": { "type": "string" }, "updated_at": { "type": "date", "default": "now" } }, "validations": [], "relations": {}, "acls": [], "methods": {} } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 { "name" : "todos" , "base" : "PersistedModel" , "idInjection" : true , "options" : { "validateUpsert" : true } , "properties" : { "name" : { "type" : "string" } , "completed" : { "type" : "boolean" , "default" : false } , "note" : { "type" : "string" } , "updated_at" : { "type" : "date" , "default" : "now" } } , "validations" : [ ] , "relations" : { } , "acls" : [ ] , "methods" : { } }

Here we can see that we don’t need to write code for a schema model, all we need to do is to run some LoopBack commands in the terminal, and LoopBack will do our job! The only code we need to write is code for custom routes or operations. Now we will play a little with this API in the explorer, to see what we can do. I hope you will enjoy this game. Start the server, and you can see loopback again create all the basic routes for the todos model:

node . # => Web server listening at: http://0.0.0.0:3000 # => Browse your REST API at http://0.0.0.0:3000/explorer 1 2 3 node . # => Web server listening at: http://0.0.0.0:3000 # => Browse your REST API at http://0.0.0.0:3000/explorer

Let’s access the explorer to see what happens with this API created with LoopBack:

Here you can see all the basic routes created by LoopBack without writing any code. Now we will try to use this explorer to test the routes. The first operation I do is to try to create a todo:

After completing the list of parameters, press “Try it out!” and see what happens:

In Response Body you can see that our object was created but…. the problem is with the “updated_at” field, because we introduced a string and not an actual object creation date. We will solve this in the file server/models/todos.json. We will need to change this:

"updated_at": { "type": "date", "default": "now" } 1 2 3 4 "updated_at" : { "type" : "date" , "default" : "now" }

Into this:

"updated_at": { "type": "date", "defaultFn": "now" } 1 2 3 4 "updated_at" : { "type" : "date" , "defaultFn" : "now" }

I changed the default in defaultFn. But… Why ? The answer comes from LoopBack. It has some predefined commands in a json file for cases just like this one. Read about LoopBack predefined commands to find out more details. For now, let’s retry creating our todo and see what happens now:

This time we get a more satisfying answer. The updated_at field has the correct value. Now let’s see how to delete an object. The first step is to make a GET request to /todos :

After this request we get a response, from which we will take the object’s id:

Take the id from the First TODO and now we need to do a DELETE request to /todos/{id} in order to get rid of our previous mistake (adding a todo with an erroneous date).

This is the request and the response for the object I want to delete from the database. We notice the response is ok and the the object was deleted from the database, but we can also check with a new GET request to /todos :

Onlt one todo object in the database (the correct one), and we can also see the erroneous object was deleted. Now our CRUD for todos is done.

Loopback Relations

Individual models are easy to understand and work with. But, in reality, models are often connected or related. When you build a real-world application with multiple models, you’ll typically need to define relations between models.

Now we have a RESTful API with two models: User and Todos. Now we want to add a relation between these two models. This relation will be many-to-one. Like what I did before adding this relation, I will use a loopback command to add this relation to the project:

slc loopback:relation ? Select the model to create the relationship from: (Use arrow keys) ❯ todos 1 2 3 slc loopback : relation ? Select the model to create the relationship from : ( Use arrow keys ) ❯ todos

After I select the model for the relation, I set its type:

? Select the model to create the relationship from: todos ? Relation type: has many belongs to has and belongs to many ❯ has one ? Enter the model name: User ? Enter the property name for the relation: users ? Optionally enter a custom foreign key: 1 2 3 4 5 6 7 8 9 ? Select the model to create the relationship from : todos ? Relation type : has many belongs to has and belongs to many ❯ has one ? Enter the model name : User ? Enter the property name for the relation : users ? Optionally enter a custom foreign key :

Selecting “has one” means that every todo will have one user.

Loopback ACL

The first step in specifying user roles is to determine what roles your application needs. Most applications will have un-authenticated or anonymous users (those who have not logged in) and authenticated users (those who have logged in). Additionally, many applications will have an administrative role that provides broad access rights. Applications can have any number of additional user roles as appropriate.

LoopBack applications access data through models, so controlling access to data means putting restrictions on models; that is, specifying who or what can read and write the data or execute methods on the models. LoopBack access controls are determined by access control lists or ACLs. Setting an ACL for the Todo model is an easy task and we can achieve it through another loopback command.

slc loopback:acl ? Select the model to apply the ACL entry to: (all existing models) ? Select the ACL scope: All methods and properties ? Select the access type: All (match all types) ? Select the role All users ? Select the permission to apply Explicitly deny access 1 2 3 4 5 6 slc loopback : acl ? Select the model to apply the ACL entry to : ( all existing models ) ? Select the ACL scope : All methods and properties ? Select the access type : All ( match all types ) ? Select the role All users ? Select the permission to apply Explicitly deny access

First we deny all users access to Todos, and afterwards we set some exceptions: users who do have access to the todos:

slc loopback:acl ? Select the model to apply the ACL entry to: todos ? Select the ACL scope: All methods and properties ? Select the access type: Read ? Select the role The user owning the object ? Select the permission to apply Explicitly grant access slc loopback:acl ? Select the model to apply the ACL entry to: todos ? Select the ACL scope: A single method ? Enter the method name create ? Select the role Any authenticated user ? Select the permission to apply Explicitly grant access slc loopback:acl ? Select the model to apply the ACL entry to: todos ? Select the ACL scope: All methods and properties ? Select the access type: Write ? Select the role The user owning the object ? Select the permission to apply Explicitly grant access 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 slc loopback : acl ? Select the model to apply the ACL entry to : todos ? Select the ACL scope : All methods and properties ? Select the access type : Read ? Select the role The user owning the object ? Select the permission to apply Explicitly grant access slc loopback : acl ? Select the model to apply the ACL entry to : todos ? Select the ACL scope : A single method ? Enter the method name create ? Select the role Any authenticated user ? Select the permission to apply Explicitly grant access slc loopback : acl ? Select the model to apply the ACL entry to : todos ? Select the ACL scope : All methods and properties ? Select the access type : Write ? Select the role The user owning the object ? Select the permission to apply Explicitly grant access

After we set access control for todos, our todos.json looks like this:

{ "name": "todos", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "properties": { "name": { "type": "string" }, "completed": { "type": "boolean", "default": false }, "note": { "type": "string" }, "updated_at": { "type": "date", "defaultFn": "now" } }, "validations": [], "relations": { "users": { "type": "hasOne", "model": "User", "foreignKey": "" } }, "acls": [ { "accessType": "*", "principalType": "ROLE", "principalId": "$everyone", "permission": "DENY" }, { "accessType": "READ", "principalType": "ROLE", "principalId": "$owner", "permission": "ALLOW" }, { "accessType": "EXECUTE", "principalType": "ROLE", "principalId": "$authenticated", "permission": "ALLOW", "property": "create" }, { "accessType": "WRITE", "principalType": "ROLE", "principalId": "$owner", "permission": "ALLOW" } ], "methods": {} } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 { "name" : "todos" , "base" : "PersistedModel" , "idInjection" : true , "options" : { "validateUpsert" : true } , "properties" : { "name" : { "type" : "string" } , "completed" : { "type" : "boolean" , "default" : false } , "note" : { "type" : "string" } , "updated_at" : { "type" : "date" , "defaultFn" : "now" } } , "validations" : [ ] , "relations" : { "users" : { "type" : "hasOne" , "model" : "User" , "foreignKey" : "" } } , "acls" : [ { "accessType" : "*" , "principalType" : "ROLE" , "principalId" : "$everyone" , "permission" : "DENY" } , { "accessType" : "READ" , "principalType" : "ROLE" , "principalId" : "$owner" , "permission" : "ALLOW" } , { "accessType" : "EXECUTE" , "principalType" : "ROLE" , "principalId" : "$authenticated" , "permission" : "ALLOW" , "property" : "create" } , { "accessType" : "WRITE" , "principalType" : "ROLE" , "principalId" : "$owner" , "permission" : "ALLOW" } ] , "methods" : { } }

I will start testing this, by going to the explorer and making a GET request on todos:

In the response we can see a message that didn’t appear before setting access control for todos. Now, to be able to see the todos, as before, we need to create a user. Make the following request:

Now we have a user who can create todos. Before you can do that, though, you need to log in with this user:

This login generates an access token which is found in the “id” field. Copy the token and put it in the access token case:

We can now start playing with todos API:

The CRUD for todos is working again, but this time you need to be logged in with a user in order to access them.

Conclusion

LoopBack is a NodeJs framework which enables you to write less code, at least compared to the likes of Express and some other frameworks. To use LoopBack it is enough to know how commands work and sometimes write some minimal code, mostly for customising a thing or another. LoopBack will create your basic routes when you add a model into the application. Overall, the idea is to work with terminal commands to effortlessly build an API. This framework is easy to learn and understand. And if you want to have it even easier, try our loopback aliases, for even more concise commands.

Free email updates! Get the latest content first. No spam. Just occasional emails with great engineering posts. Send me great engineering posts