{"id":21452,"date":"2016-12-28T13:31:29","date_gmt":"2016-12-28T13:31:29","guid":{"rendered":"http:\/\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1"},"modified":"2016-12-28T13:31:29","modified_gmt":"2016-12-28T13:31:29","slug":"how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1","title":{"rendered":"How to build a CMS with Adonis: A Laravel-like MVC framework for Node &#8211; Part 1"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Even though Node is fun, easy and cheap to work with, we spend a lot of time writing boilerplate codes because structure and organization is missing.<\/p>\n<p>What happened to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Convention_over_configuration\">Convention Over Configuration<\/a>?<\/p>\n<p>While Node is simple, it requires you to make a lot of decisions, which ultimately causes confusion because it leaves you with several options. Languages like PHP, Ruby, C# and Python have one or more <a href=\"https:\/\/en.wikipedia.org\/wiki\/Model%E2%80%93view%E2%80%93controller\">Molde-View-Controller (MVC)<\/a> frameworks, such as Laravel, Rails, ASP.Net and Django. These help developers to achieve structure and write maintainable code with these languages. That was not the case for Node until AdonisJs was introduced.<\/p>\n<p><a href=\"http:\/\/www.adonisjs.com\/\">AdonisJs<\/a> can help with these challenges. AdonisJs is an MVC framework for Node that is modeled after the popular PHP\u2019s <a href=\"https:\/\/laravel.com\/\">Laravel<\/a> framework with a few concepts from Rails as well.<\/p>\n<p>Now, let\u2019s look at how you can use Adonis to create a simple content management system (CMS) performing CRUD (create, read, update and delete) operations.<\/p>\n<h2>Adonis Setup<\/h2>\n<p>Before we write example code, we need to get Adonis on our machines and review a typical Adonis application files structure.<\/p>\n<h3>Adonis Installation &amp; New Projects<\/h3>\n<p>Adonis has a command-line interface (CLI) that makes most utility tasks easy. We can use npm to install the CLI globally:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>$ npm install -g adonis-cli\n<\/code><\/pre>\n<p><em>Node v4.0.0 and npm are the only requirements for installing Adonis to your computer.<\/em><\/p>\n<p>With Adonis installed and the Adonis command available, we can use it to create a new project:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>$ adonis new simple-blog\n$ cd simple-blog\n$ npm start\n<\/code><\/pre>\n<p>The <code>new<\/code> command creates a new Adonis project with all the necessary files and content to get us started. It also installs the <code>npm<\/code> dependencies into the <code>node_modules<\/code> folder, so there is no need to run <code>npm install<\/code>.<\/p>\n<p>You can create an Adonis project without using the CLI command. This option is considered manual and can be achieved by getting the boilerplate from GitHub and installing the dependencies:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>$ git clone https:\/\/github.com\/adonisjs\/adonis-app simple-blog\n\n$ cd simple-blog\n\n$ npm install\n\n$ npm start\n<\/code><\/pre>\n<p>The purple screen at <code>localhost:3333<\/code> indicates that a new app was successfully created:\n<img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary\/image\/upload\/w_700\/successfully_created_adonis_app.png\" alt=\"Successfully created app\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"700\" height=\"446\"\/><\/p>\n<h3>Directory Structure<\/h3>\n<p>The directory structure of an Adonis project can be intimidating at first. But, that\u2019s the case with any framework that implements the MVC architecture. With time, you will understand how to navigate it. Here\u2019s a top-level overview for your reference:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>\u251c\u2500\u2500 app # Your code's home\n\u2502   \u251c\u2500\u2500 Commands # Ace Commands\n\u2502   \u251c\u2500\u2500 Http # Controllers and Routes\n\u2502   \u251c\u2500\u2500 Listeners # Event Listeners\n\u2502   \u251c\u2500\u2500 Model # Database Models\n\u251c\u2500\u2500 bootstrap # Application setup logistics \n\u251c\u2500\u2500 config # All Configuration lives here\n\u251c\u2500\u2500 database\n\u2502   \u251c\u2500\u2500 migrations # Database Migrations\n\u2502   \u2514\u2500\u2500 seeds # Dummy data\n\u251c\u2500\u2500 providers # Service Providers\n\u251c\u2500\u2500 public # Client files\n\u251c\u2500\u2500 resources\n\u2502   \u2514\u2500\u2500 views # Application views\n\u251c\u2500\u2500 storage # Temporary Logs and Sessions\n<\/code><\/pre>\n<h2>Database &amp; Migrations<\/h2>\n<p>The <code>Sqlite<\/code> database is a good choice for development purposes and very small scale applications. We can make use of it by installing via npm and telling Adonis to use it as our default database via the database config.<\/p>\n<pre class=\"js-syntax-highlighted\"><code>npm i --save sqlite3\n<\/code><\/pre>\n<p>The above command runs the installation process for <code>sqlite3<\/code>. Now we must tell Adonis to make use of the database we just installed. Conveniently, Adonis already uses <code>sqlite3<\/code> as the default database:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/config\/database.js<\/span>\n<span class=\"hljs-built_in\">module<\/span>.exports = {\n    <span class=\"hljs-comment\">\/\/ Use Connection defined in .env file or<\/span>\n    <span class=\"hljs-comment\">\/\/ sqlite if none<\/span>\n    <span class=\"hljs-attr\">connection<\/span>: Env.get(<span class=\"hljs-string\">'DB_CONNECTION'<\/span>, <span class=\"hljs-string\">'sqlite'<\/span>),\n    <span class=\"hljs-comment\">\/\/ sqlite configuration<\/span>\n    <span class=\"hljs-attr\">sqlite<\/span>: {\n      <span class=\"hljs-attr\">client<\/span>: <span class=\"hljs-string\">'sqlite3'<\/span>,\n      <span class=\"hljs-attr\">connection<\/span>: {\n        <span class=\"hljs-attr\">filename<\/span>: Helpers.databasePath(<span class=\"hljs-string\">'development.sqlite'<\/span>)\n      },\n      <span class=\"hljs-attr\">useNullAsDefault<\/span>: <span class=\"hljs-literal\">true<\/span>\n    },\n    ...\n }\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Migration is not a core MVC architecture concept, but it makes it easy to share schemas in a declarative manner. Rather than defining the schema in our databases directly, we define them in a file. This file can then be shared among team members.<\/p>\n<p>To create a <code>posts<\/code> migration, run:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>.\/ace make:migration posts --create=posts\n<\/code><\/pre>\n<p>The command will create a migration class file named <code>[TIMESTAMP]_posts.js<\/code> in <code>database\/migrations<\/code>.<\/p>\n<p>The class has an <code>up<\/code> and <code>down<\/code> method. <code>up<\/code> is called when we run migrations to create the database table, while <code>down<\/code> is called when we want to tear down the table.<\/p>\n<p>Currently, the schema is defined with nothing but methods that will create the primary key <code>(increments())<\/code> and another to create timestamps <code>(timestamps())<\/code>. Let\u2019s add something that a post should have:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/database\/migrations\/&#91;TIMESTAMP]_posts.js<\/span>\n...\nup () {\n    <span class=\"hljs-keyword\">this<\/span>.create(<span class=\"hljs-string\">'posts'<\/span>, (table) =&gt; {\n      table.increments();\n      table.timestamps();\n      <span class=\"hljs-comment\">\/\/ New columns' methods <\/span>\n      table.string(<span class=\"hljs-string\">'title'<\/span>);\n      table.string(<span class=\"hljs-string\">'body'<\/span>);\n      table.string(<span class=\"hljs-string\">'image'<\/span>);\n    })\n  }\n...\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We are calling the <code>string<\/code> method on the <code>table<\/code>, which was passed in to the <code>create<\/code> closure. This method will create a <code>column<\/code> of string type (or <code>varchar<\/code>, as the case may be) and the column will be named after whatever value  was passed in.<\/p>\n<p>You can then execute the migration run command to begin migration:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>.\/ace migration:run\n<\/code><\/pre>\n<p>After a successful migration, you should get a message in the console saying so:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>\u2714 Database migrated successfully in 120 ms\n<\/code><\/pre>\n<h2>Database Models<\/h2>\n<p>Now let\u2019s focus on the <code>M<\/code> in MVC, which stands for Models. Models are stored in the <code>app\/Model<\/code> folder and can be created using the ace command as well:<\/p>\n<pre class=\"js-syntax-highlighted\"><code>.\/ace make:model Post\n<\/code><\/pre>\n<p><strong>Note<\/strong>: Convention over configuration is a software engineering paradigm in which developers are not required to make too many decisions. Each Model needs to be mapped to a migration. Adonis uses this paradigm, so rather than defining it manually, it helps you make the decision. A Post will map to a posts migration, which is a plural lowercase. We are never compelled to define a config of what model maps to what migration.<\/p>\n<p>Here\u2019s what our model looks like:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/Model\/Post.js<\/span>\n<span class=\"hljs-keyword\">const<\/span> Lucid = use(<span class=\"hljs-string\">'Lucid'<\/span>)\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Post<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Lucid<\/span> <\/span>{\n\n}\n\n<span class=\"hljs-built_in\">module<\/span>.exports = Post\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><code>Lucid<\/code> is the library that Adonis uses to manage Models.<\/p>\n<p>The above <code>Post<\/code> model might seem empty. However, because it will eventually be mapped to our <code>posts<\/code> migration, we still gain access to the table\u2019s columns. We will discuss this later in the article.<\/p>\n<h2>Creating Posts<\/h2>\n<p>Now that Migrations and Models have been taken care of, we have a store. Henceforth, we will be playing with HTTP requests and responses using routes, controllers and views.<\/p>\n<p>Our first two routes are going to handle creating new posts:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ .\/app\/Http\/routes.js<\/span>\n\n<span class=\"hljs-comment\">\/\/GET route to send the new post form<\/span>\nRoute.get(<span class=\"hljs-string\">'\/new'<\/span>, <span class=\"hljs-string\">'PostController.new'<\/span>);\n<span class=\"hljs-comment\">\/\/POST route to send form data to the server<\/span>\nRoute.post(<span class=\"hljs-string\">'\/create'<\/span>, <span class=\"hljs-string\">'PostController.create'<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The route method takes two arguments \u2013 the route URL and the controller actions. We now need to create the controllers with the new and create action methods:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ Import Model<\/span>\n<span class=\"hljs-keyword\">const<\/span> Post = use(<span class=\"hljs-string\">'App\/Model\/Post'<\/span>);\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">PostController<\/span> <\/span>{\n    <span class=\"hljs-comment\">\/\/ New action method<\/span>\n    * <span class=\"hljs-keyword\">new<\/span>(request, response) {\n        <span class=\"hljs-comment\">\/\/ Send a view<\/span>\n        <span class=\"hljs-keyword\">yield<\/span> response.sendView(<span class=\"hljs-string\">'post\/new'<\/span>); <span class=\"hljs-comment\">\/\/ .\/resources\/views\/post\/new.njk<\/span>\n    }\n\n    <span class=\"hljs-comment\">\/\/ Create Action method<\/span>\n    * create(request, response) {\n        <span class=\"hljs-comment\">\/\/ Filter relevant post data<\/span>\n        <span class=\"hljs-keyword\">const<\/span> postData = request.only(<span class=\"hljs-string\">'title'<\/span>, <span class=\"hljs-string\">'body'<\/span>);\n        <span class=\"hljs-comment\">\/\/ Create and store post<\/span>\n        <span class=\"hljs-keyword\">yield<\/span> Post.create(postData);\n        <span class=\"hljs-comment\">\/\/ Redirect to home page<\/span>\n        response.redirect(<span class=\"hljs-string\">'\/'<\/span>);\n    }\n}\n\n<span class=\"hljs-built_in\">module<\/span>.exports = PostController\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<ul>\n<li>We first import the <code>Post<\/code> Model to our controller. This is because, the <code>create<\/code> action method will make use of this Model to create and store new posts.<\/li>\n<li>The first controller action \u2013 <code>new<\/code> \u2013 sends a <code>Nunjucks<\/code> view that contains a template for the post form. The syntax is known as <code>Nunjucks<\/code> with  an<code>.njk<\/code> extension and that is what Adonis uses for templates.<\/li>\n<li>The second controller action \u2013 <code>create<\/code> \u2013 receives data from the browser through the form and stores it using the <code>Post<\/code> Model.<\/li>\n<\/ul>\n<p>The <code>* ... yield<\/code> syntax is an EcmaScript upcoming feature called Generators. They are a different kind of function and Adonis uses it to simplify a lot of async tasks.<\/p>\n<p>Here is what the form in the view looks like:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">{% extends 'master' %}\n\n{% block content %}\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>New Post<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n  {{ form.open({action: 'PostController.create'}) }}\n\n    {{ csrfField }}\n\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"ui form\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n            {{ form.label('Post Title') }}\n            {{ form.text('title', null) }}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"field\"<\/span>&gt;<\/span>\n            {{ form.label('Body') }}\n            {{ form.textarea('body', null) }}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n        {{ form.submit('Create', 'create', { class: 'ui blue button' }) }}\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n  {{ form.close() }}\n{% endblock %}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>You can visit the <code>\/new<\/code> route to create a new post. This is what our data looks like in the database viewer:\n<img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary\/image\/upload\/w_700\/adonis_database_viewer.png\" alt=\"Our data in the database viewer\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"700\" height=\"443\"\/><\/p>\n<h2>Next Up<\/h2>\n<p>Here we introduced Adonis and some of its basic concepts. In the second part of this article, we\u2019ll share how easy it is to handle image uploads when creating a new post, how to read\/update existing posts and display them, and how to delete posts from the store.<\/p>\n<table>\n<tr>\n<td style = \"padding: 5px;\">\n<img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary\/image\/upload\/c_thumb,w_100\/christian_nwamba.jpg\" alt=\"Christian Nwamba\" title=\"Christian Nwamba\"><\/img><\/td>\n<td style = \"padding: 10px;\"><i><a href=\"https:\/\/twitter.com\/codebeast\" target=\"_new\">Christian Nwamba<\/a> is a code beast, with a passion for instructing computers and understanding it&#8217;s language. In his next life, Chris hopes to remain a computer programmer.<\/i><\/td>\n<\/tr>\n<\/table>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":21453,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[124,177,214],"class_list":["post-21452","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-frameworks","tag-javascript","tag-node"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Learn about AdonisJs and how to set up a CMS with it<\/title>\n<meta name=\"description\" content=\"This blog post describes the Adonis framework, an MVC framework for Node, similar to Laravel, and how to create a simple content management system (CMS) with it.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to build a CMS with Adonis: A Laravel-like MVC framework for Node - Part 1\" \/>\n<meta property=\"og:description\" content=\"This blog post describes the Adonis framework, an MVC framework for Node, similar to Laravel, and how to create a simple content management system (CMS) with it.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2016-12-28T13:31:29+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"700\" \/>\n\t<meta property=\"og:image:height\" content=\"330\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"How to build a CMS with Adonis: A Laravel-like MVC framework for Node &#8211; Part 1\",\"datePublished\":\"2016-12-28T13:31:29+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1\"},\"wordCount\":15,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA\",\"keywords\":[\"Frameworks\",\"Javascript\",\"Node\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2016\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1\",\"url\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1\",\"name\":\"Learn about AdonisJs and how to set up a CMS with it\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA\",\"datePublished\":\"2016-12-28T13:31:29+00:00\",\"description\":\"This blog post describes the Adonis framework, an MVC framework for Node, similar to Laravel, and how to create a simple content management system (CMS) with it.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA\",\"width\":700,\"height\":330},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to build a CMS with Adonis: A Laravel-like MVC framework for Node &#8211; Part 1\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Learn about AdonisJs and how to set up a CMS with it","description":"This blog post describes the Adonis framework, an MVC framework for Node, similar to Laravel, and how to create a simple content management system (CMS) with it.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1","og_locale":"en_US","og_type":"article","og_title":"How to build a CMS with Adonis: A Laravel-like MVC framework for Node - Part 1","og_description":"This blog post describes the Adonis framework, an MVC framework for Node, similar to Laravel, and how to create a simple content management system (CMS) with it.","og_url":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1","og_site_name":"Cloudinary Blog","article_published_time":"2016-12-28T13:31:29+00:00","og_image":[{"width":700,"height":330,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA","type":"image\/png"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1"},"author":{"name":"","@id":""},"headline":"How to build a CMS with Adonis: A Laravel-like MVC framework for Node &#8211; Part 1","datePublished":"2016-12-28T13:31:29+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1"},"wordCount":15,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA","keywords":["Frameworks","Javascript","Node"],"inLanguage":"en-US","copyrightYear":"2016","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1","url":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1","name":"Learn about AdonisJs and how to set up a CMS with it","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA","datePublished":"2016-12-28T13:31:29+00:00","description":"This blog post describes the Adonis framework, an MVC framework for Node, similar to Laravel, and how to create a simple content management system (CMS) with it.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA","width":700,"height":330},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/how_to_build_a_cms_with_adonis_a_laravel_like_mvc_framework_for_node_part_1#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to build a CMS with Adonis: A Laravel-like MVC framework for Node &#8211; Part 1"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649724573\/Web_Assets\/blog\/adonis_blog_post\/adonis_blog_post.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21452","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=21452"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/21452\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/21453"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=21452"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=21452"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=21452"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}