Hey there! We have posted a newer version of this blog with the Strapi Beta version which is available here.

The evolution of CDN (Content Delivery Networks) and business need to scale multi-channel ways of interacting with the customer have made the traditional CMS obsolete. Organizations are using JAMstack to build responsive websites & CDN to deliver them where the customer is. With the evolution of the 5G and data-intensive applications such as AR, VR, drones, etc. new technologies will emerge that will deliver services closest to where the customer resides.

One of the key components in this JAMStack based architecture is a headless CMS system. Strapi is an open-source headless CMS framework to build APIs with minimum efforts and in almost no time. In this tutorial, we will build a simple headless CMS with Strapi and create API endpoints in less than 5 minutes. In the next tutorial, we will explore GraphQL and how to use it with Strapi.

Prerequisites

You will need the NodeJS version 10.x, NPM version 6.x and MongoDB > 3.6 installed on your machine if you plan to follow along with the tutorial. You can use Docker image for Mongo or run it locally. Strapi also supports MariaDB, MySQL, and PostgreSQL but as of this writing, MongoDB is supported as a first-class citizen. Therefore, for the scope of this article, we’ll stick to MongoDB. Strapi works well with MacOS and Linux based operating systems and for Windows, it has some support via WSL.

Installation

Once the node and NPM is setup, strapi can be installed and verified by the following commands:

$ npm install strapi@alpha -g
$ strapi --version
3.0.0-alpha.25.2

Creating The First Project

Once the strapi is installed, we’re ready to create our very first project using strapi. Create the project by running the new command:

$ strapi new mongoose-project
🚀 Starting to create your Strapi application.

? Choose your installation type Custom (manual settings)
? Choose your main database: MongoDB
? Database name: mongoose-project
? Host: 127.0.0.1
? +srv connection: false
? Port (It will be ignored if you enable +srv): 27017
? Username: 
? Password: 
? Authentication database (Maybe "admin" or blank): 
? Enable SSL connection: false

Select the appropriate options. In this case, I’ve selected a custom installation with MongoDB as the database. Since MongoDB is running locally on my machine, the host IP is that of the localhost and other configurations are defaults. Setup will proceed and connect to MongoDB:

⏳ Testing database connection...
It might take a minute, please have a coffee ☕️

The app has been connected to the database successfully!

🏗  Application generation:
✔ Copy dashboard
✔ Install plugin settings-manager.
✔ Install plugin content-type-builder.
✔ Install plugin content-manager.
✔ Install plugin users-permissions.
✔ Install plugin email.
✔ Install plugin upload.
✔ Link strapi dependency to the project.
✔ Link strapi-utils dependency to the project.
✔ Link strapi-hook-mongoose dependency to the project.

👌 Your new application mongoose-project is ready at /home/omkar/work/blogs/mongoose-project.

⚡️ Change directory:
$ cd mongoose-project

⚡️ Start application:
$ strapi start

Let’s change the directory to the newly created project and verify the information provided while creating the project in a database.json file:

$ cd mongoose-project
$ cat config/environments/development/database.json

Starting The Strapi Instance

We are now ready to start the Strapi with MongoDB as it’s backend:

$ strapi start
[2019-04-25T05:25:03.413Z] info uriOptions:  {}
(node:11334) DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead.
[2019-04-25T05:25:03.977Z] info File changed: /home/omkar/work/blogs/mongoose-project/plugins/users-permissions/config/actions.json
[2019-04-25T05:25:03.997Z] info Time: Thu Apr 25 2019 10:55:03 GMT+0530 (India Standard Time)
[2019-04-25T05:25:03.997Z] info Launched in: 2934 ms
[2019-04-25T05:25:03.997Z] info Environment: development
[2019-04-25T05:25:03.997Z] info Process PID: 11334
[2019-04-25T05:25:03.997Z] info Version: 3.0.0-alpha.25.2 (node v10.15.3)
[2019-04-25T05:25:03.997Z] info To shut down your server, press <CTRL> + C at any time

[2019-04-25T05:25:03.997Z] info ☄️  Admin panel: http://localhost:1337/admin [2019-04-25T05:25:03.997Z] info ⚡️ Server: http://localhost:1337 [2019-04-25T05:25:04.015Z] debug HEAD index.html (10 ms) [2019-04-25T05:25:04.017Z] info ⏳ Opening the admin panel...

When you open the admin panel (http://localhost:1337/admin) for the first time, you need to create a new user via the registration page:

After creating the new user, strapi will automatically log you in.

Users – The Users Permissions Plugin

Before we move on to create our first entity, let’s take a look at the Users tab that we see on the left-hand panel. This tab comes from a plugin called users-permissions. In a nutshell, this plugin provides, in the increasing level of abstraction, permissions, roles, and users for us to work with. Permissions can be fine grained and we can restrict the permission precisely to a specific action for a specific entity. A set of these permissions can be associated with a role which can then be applied to multiple users.

If you click on the “Users” tab, you’ll see the first user that we created when we registered exists as a record. You should also see that this user has an administrator role; strapi registers the first user as an administrator. The “Roles & Permissions” tab on the left provides the interface necessary to create roles, permissions, etc.

Creating The First Content-Type

To create a content-type or an entity, click on the Content-Type Builder from left pane then click on Add Content Type. In this blog, I am creating 2 content types, Article, and Author. After creating content types, add appropriate fields in those. For example, the article content type can have fields like title(string), contents(text), publishDate(date) etc. The author content type can have fields like a name (string), designation(string), etc.

Along with this, we also need to create many to many relationships between author and article since one article could be written by more than one author and one author can write multiple articles. We can achieve this by adding a field in either author or article of type relation and then by selecting the type of relation as many to many.

We can perform CRUD operations on the created content types from the UI. However, since we’ve already created these content types, we can use REST APIs to perform these operations. The documentation (swagger) plugin enables easy consumption of the APIs created for the entity types.

Swagger – The Documentation Plugin

Let’s install the documentation plugin. To do so, we need to stop the running strapi server by pressing “Ctrl + c” and then from the project directory itself run the following command.

$ strapi install documentation
✔ The documentation plugin has been successfully installed.

Once again start the strapi server by running

strapi start

On the left-hand side panel, under plugins, we should now be able to see the documentation option. When we click on that, we see a few options like the jwt token, the link to open the documentation, the button to update the documentation, etc. Clicking on open the documentation will take us to the documentation page. Here we can get the details of the content types that we have created and a simple mechanism to test and consume those content types.

Before we can try out the APIs, we need to authorize the swagger client. We can do so by clicking on authorize button on the right-hand side of the documentation page and adding the jwt token from the documentation plugin. After doing so, we can start using the swagger client from the documentation page.

Now that we have a strapi server running, we can create content types (entities or models) in virtually no time. This allows us great flexibility to work on the user interface side without worrying much about the backend. These endpoints can be consumed by any frontend applications via HTTP REST.

Diving Deeper

We’ve seen that strapi generates some code that allows us to perform CRUD operations. But where does that code reside? In the project directory, we can see that there is a directory called api. In this directory, strapi will create a new directory for each of the content type (model) that we create. For example, in our case, there are 2 directories, article, and author:

omkar@Omkar mongoose-project $ cd api/
index_image: /assets/img/Blog/
omkar@Omkar api $ ls 
article  author

In each of those directories, we can find the code specific to those entities.

omkar@Omkar api $ ls article/
config  controllers  documentation  models  services

In the config directory which will find the routes.json file, which is useful for finding out the code for a specific end-point. Example routes.json file for the article entity is shown below.

{
  "routes": [
    {
      "method": "GET",
      "path": "/articles",
      "handler": "Article.find",
      "config": {
        "policies": []
      }
    },
    {
      "method": "GET",
      "path": "/articles/count",
      "handler": "Article.count",
      "config": {
        "policies": []
      }
    },
    {
      "method": "GET",
      "path": "/articles/:_id",
      "handler": "Article.findOne",
      "config": {
        "policies": []
      }
    },
    ...
    ]
}

The above file has been shortened for brevity. The documentation directory contains the generated files used by the documentation plugin to show the Swagger UI.

In models, controller, and services directories, we will find a structure similar to that of config. We will have a directory per entity within which the generated code will reside. The entity directories under models will also have a settings.json file within them. This file will be named as <Modelname>.settings.json. It contains the details about that entity and the attributes that belong to that entity, including the related attributes. One needs to be careful in making changes to the related attributes. Since strapi will not work if the relationship attributes in both related entities do not match in type. The Article.settings.json has been added as an example. Beyond what the UI can provide – these files can be directly edited if needed for specific use cases.

{
  "connection": "default",
  "collectionName": "",
  "info": {
    "name": "article",
    "description": ""
  },
  "options": {
    "timestamps": true
  },
  "attributes": {
    "title": {
      "default": "",
      "type": "string"
    },
    "contents": {
      "default": "",
      "type": "text"
    },
    "publishDate": {
      "default": "",
      "type": "date"
    },
    "authors": {
      "collection": "author",
      "via": "articles"
    }
  }
}

File Upload – Working with files

One of the crucial things for any CMS system is the file upload functionality. Strapi comes with a built-in file upload plugin. On the left-hand panel, there’s an option for file upload. It provides an interface for uploading the file. These files are placed in the directory <project_dir>/public/uploads.

Next Steps

Strapi allows us to quickly build models without worrying about the backend side of things. While this itself is a great value addition, one of the best things about strapi is its support of GraphQL. Strapi provides a GraphQL Plugin and Playground allows us to use GraphQL queries with the models that we’ve created. In the next article, we will cover GraphQL queries in the context of Strapi.