An Intro to GraphQL API

An Intro to GraphQL API

Today, I want to show you how to build serverless backends with the Hasura GraphQL API. After looking at GraphQL and its advantages, we will learn how to create new tables for our API. Next, we want to fill our database with real data. Of course, we want to have relationships between our tables. Finally, we will learn how to manipulate the data using mutations. No previous knowledge of GraphQL is required to follow this tutorial.


Table of Contents

  • About GraphQL
  • About Hasura
  • Creating a project
  • Create tables
  • Insert data
  • Queries
  • Relationships
  • Object relationship
  • Array relationship
  • Many-to-many relationship
  • Mutations

About GraphQL

GraphQL is a typed query language for APIs. More and more tech companies, including tech giants like Facebook, Twitter and GitHub, are switching from common REST apis to GraphQL solutions. The main advantages of GraphQL over other API architectures like REST are:

  • Get the exact data you need with easily understandable queries
  • Get reliable results and helpful error messages
  • Fast and lightweight applications because the data is accessed directly instead of through a server
  • Get several resources with one query without using several URLs and chaining resources
  • Use one endpoint for all data and typed fields for correct data access
  • Add GraphQL easily to your existing applications

Here is a sample query:

And its corresponding result:

As you can see, the query is very intuitive and the result is predictable. You get exactly what you ask for: not more and not less.

As web applications tend to get more complex, there is a growing need for fast and easily maintainable solutions. So this is probably a good time to have a glimpse at GraphQL. But how do we use it? Do I need to build my own server? How can we connect it to our frontend? Don’t worry, we’ll get there.


About Hasura

Hasura provides you with an open source GraphQL engine that runs in a Docker container. Hasura connects to the Postgres database that is created with the project. You can also run Hasura GraphQL on top of an existing project. This can in particular be useful if you want to migrate to GraphQL, as it allows you to do the migration in smaller steps.

There are several options as to where you can deploy your GraphQL API: Heroku, Docker, Digital Ocean, Azure, AWS and Google Cloud.


Creating a project

In this post, we will get started with Heroku. We are going to create a Harry Potter api 🤓

https://dashboard.heroku.com/new?button-url=https%3A%2F%2Fdocs.hasura.io%2F1.0%2Fgraphql%2Fmanual%2Fgetting-started%2Fheroku-simple.html&template=https%3A%2F%2Fgithub.com%2Fhasura%2Fgraphql-engine-heroku

On the dashboard, choose a (unique) name for your api and click the “Deploy” button:

That was easy, right? Now, if you scroll to the bottom you will see this:

When you click on “View”, the Hasura console will open in the GraphiQL tool, which is also one of the features that makes your life with GraphQL super easy. In the Hasura console, you can create tables and test queries. Let’s have a quick look at the console:

  1. This is the (only) endpoint used to interact with the api. When we later poll data from external services, this is the URL we need to access. And yes, you’ve seen correctly: requests to GraphQL are always POST requests.
  2. Here, you can add request headers. If you want to add authentication (e.g. with JWT tokens) later, you can add the headers here.
  3. This is the field where you can test queries.
  4. Here, the results will be displayed.
  5. We will go there next, to create our first table.

Create tables

We first want to create a table to store movies. Let’s do that!

  1. Name of the table.
  2. It is recommended to add an ID to all tables. You can use an Integer or UUID. We’ll use UUID. Because we don’t want to pass an ID every time we create an object, Hasura offers a method to create it automatically.
  3. Adding more fields.
  4. We need to define the primary key for each table. This will be our ID.

Create the following two tables:

characters (id: UUID, name: Text, hair_color: Text, house: Text, wizard: Boolean, birth_year: Integer, patronus: Text)

actors (id: UUID, name: Text, birth_year: Integer, awards: Integer)


Insert data

Let’s add some data data:

Add one other character, as well as two movies and two actors.


Queries

Now that we have some data, we can make our first query in GraphiQL.

It will return the two characters that we have already inserted into our characters table.

There are a lot of different constraints you can add to your queries. For example, you can make sure only to get a certain number of objects. Or only get the objects where a certain condition is true. All this is very well documented by Hasura: https://docs.hasura.io/1.0/graphql/manual/queries/simple-object-queries.html. You can read through it and tweak your queries, so that they return different results. You might have to add some more data in order to do so.


Relationships

Currently, we have three tables that are all independent from each other. With a query, we can retrieve movies, characters and actors. But we cannot retrieve the movies with their characters and in turn the respective actors. In order to do this, we need to define relationships.

There are two different types of relationships: the object relationship and the array relationship. The object relationship is a one-to-one relationship. For example, a character has a single nested resource that is called actor. The array relationship is a one-to-many relationship. For example, a movie will have an array of nested resources called scenes.


Object relationship

Let’s first model the relationship between characters and actors. The first step is to add a actor_id to the characters table:

After adding the column, we need to edit it and to make sure that the actor_id is actually a foreign key, pointing to the actors table.

When we go to the tab “Relationships” on characters, a suggested relationship will appear. That’s right — Hasura detects foreign keys automatically and makes suggestions regarding relationships. As a name, we’ll take “actor”.

Next, we want to connect the characters with the corresponding actors in our database. When we now look into the characters table, we can see that the actor_id for the previously created characters is NULL. We can now edit the data and add the ids of the actors that correspond to these characters.

Now the characters and their actors are linked. Now we can access fields from the actors along with the characters in the same query:


Array relationship

The array relationship is a one-to-many relationship. This means that one object of a table can have several objects of another table. Let’s say in our example, one movie can have several scenes and each scene belongs to one movie. A scene has an id, a name, a location and a movie_id. So let’s create a new table called “scenes” to our database:

Great! Now, just like we did before, we need to modify the table and make the movie_id a foreign key:

When we now go to the movies table and click on “Relationships”, we can see the suggested array relationship for scenes. Let’s add this relationship and call it “scenes”. This is all that is necessary to create an array relationship. To test it, insert some rows into the scenes table.


Many-to-many relationship

As explained before, the array relationship is a one-to-many relationship. However, in our case of movies and characters, we have a many-to-many relationship. One movie can have several characters and one character can appear in several movies. For this scenario, we need to create a join table that we will call “movie_characters” in which we can store the relationship between one movie and one character. Let’s create the “movie_characters” table that has an ID, a movie_id and a character_id.

From the perspective of the movie_characters table, we need object relationships to both the movies table and the characters table. This is because in each movie_character, there is one movie and one character stored.

Like above, edit both the movie_id and the character_id on the movie_character table and tick the checkbox for them to be foreign keys. Then add the correct reference table and the reference column. For the movie_id, the reference table is movies and for the character_id, the reference table is characters. For both reference columns, it will be ID.

When you now click the tab “Relationships”, you’ll see two suggestions for object relationships. Add them both and call them “movie” and “character”. Once added, it should look like this:

Of course, like above, we need to create the relationships with our data. Create new rows in the movie_characters table for each movie — character relationship, using their IDs.

Yayy, now all our tables are modelled with their correct relationships. Let’s test it with a query.

Isn’t this neat? With just one query, we are able to access several resources with those fields we want. Compared to REST where usually the whole object is returned, we can reduce the query to the essentials. This makes the API more efficient, more lightweight and easier to handle.


Mutations

So far, we learned how to get data from our API. But what about adding new data? In our case, we might want to add a movie or a character. For this, we need mutations. Mutations are easy to use with the GraphQL API and just like queries, we can try them in the Hasura console:

Let me explain what is happening here. Inside a mutation, we can call different methods like insert, update or delete on the resources that we store in our database. In our case, we want to insert a new movie. We need to pass the movies as objects and it is possible to insert several objects in one mutation. In the end, we need to return something, which is the ID of the newly created object in our example.

Again, there is full documentation on mutations on the Hasura website: https://docs.hasura.io/1.0/graphql/manual/mutations/index.html. Go through the examples and try some other mutations, like deleting a movie.

That is it for now. I hope you had fun learning about GraphQL and that you are eager to extend your Harry Potter API with lots of new tables and data. If something is not clear, you can always send me an email at [email protected] or message me over Twitter: https://twitter.com/rubydwarf.

Stay tuned for updates. I will soon publish another blogpost on how to connect your Hasura backend to your VueJS frontend 🎉


Learn More

The Modern GraphQL Bootcamp (Advanced Node.js)

NodeJS - The Complete Guide (incl. MVC, REST APIs, GraphQL)

GraphQL with React: The Complete Developers Guide

An introduction GraphQL with AWS AppSync

GraphQL API with AWS and Use with React

GraphQL Tutorial: Understanding Spring Data JPA/SpringBoot

Getting started with GraphQL and TypeScript

Build a Simple Web App with Express, Angular, and GraphQL

Originally published by Marion Schleifer at https://medium.com

Laravel 5.8 Ajax CRUD tutorial using Datatable JS

Laravel 5.8 Ajax CRUD tutorial using Datatable JS

We will use yajra datatable to list a records with pagination, sorting and filter (search). we will use bootstrap modal for create new records and update new records. we will use resource routes to create crud (create read update delete) application in laravel 5.8.

I will provide you step by step guide to create ajax crud example with laravel 5.8. you just need to follow few step to get c.r.u.d with modals and ajax. you can easily use with your laravel 5.8 project and easy to customize it.

You can see bellow preview of ajax crud app.

List Page

Create Page

Edit Page

Step 1 : Install Laravel 5.8

first of all we need to get fresh Laravel 5.8 version application using bellow command, So open your terminal OR command prompt and run bellow command:

<pre class="ql-syntax" spellcheck="false">composer create-project --prefer-dist laravel/laravel blog </pre>

Step 2 : Install Yajra Datatable Package

We need to install yajra datatable composer package for datatable, so you can install using following command:

<pre class="ql-syntax" spellcheck="false">composer require yajra/laravel-datatables-oracle </pre>

After that you need to set providers and alias.

config/app.php

<pre class="ql-syntax" spellcheck="false">'providers' => [
....

Yajra\DataTables\DataTablesServiceProvider::class,

]

'aliases' => [

....

'DataTables' =&gt; Yajra\DataTables\Facades\DataTables::class,

]
</pre>

Step 3: Update Database Configuration

In second step, we will make database configuration for example database name, username, password etc for our crud application of laravel 5.8. So let’s open .env file and fill all details like as bellow:

.env

<pre class="ql-syntax" spellcheck="false">DB_CONNECTION=mysql

DB_HOST=127.0.0.1

DB_PORT=3306

DB_DATABASE=here your database name(blog)

DB_USERNAME=here database username(root)

DB_PASSWORD=here database password(root)
</pre>

Step 4: Create Table

we are going to create ajax crud application for product. so we have to create migration for “products” table using Laravel 5.8 php artisan command, so first fire bellow command:

<pre class="ql-syntax" spellcheck="false">php artisan make:migration create_products_table --create=products
</pre>

After this command you will find one file in following path “database/migrations” and you have to put bellow code in your migration file for create products table.

<pre class="ql-syntax" spellcheck="false"><?php

use Illuminate\Support\Facades\Schema;

use Illuminate\Database\Schema\Blueprint;

use Illuminate\Database\Migrations\Migration;

class CreateProductsTable extends Migration

{

/**

 * Run the migrations.

 *

 * @return void

 */

public function up()

{

    Schema::create('products', function (Blueprint $table) {

        $table-&gt;increments('id');

        $table-&gt;string('name');

        $table-&gt;text('detail');

        $table-&gt;timestamps();

    });

}



/**

 * Reverse the migrations.

 *

 * @return void

 */

public function down()

{

    Schema::dropIfExists('products');

}

}
</pre>

Now you have to run this migration by following command:

<pre class="ql-syntax" spellcheck="false">php artisan migrate
</pre>

Step 5: Create Resource Route

Here, we need to add resource route for product ajax crud application. so open your “routes/web.php” file and add following route.

routes/web.php

<pre class="ql-syntax" spellcheck="false">Route::resource('ajaxproducts','ProductAjaxController');
</pre>

Step 6: Create Controller and Model

In this step, now we should create new controller as ProductAjaxController. So run bellow command and create new controller.

So, let’s copy bellow code and put on ProductAjaxController.php file.

app/Http/Controllers/ProductAjaxController.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App\Http\Controllers;

use App\Product;

use Illuminate\Http\Request;

use DataTables;

class ProductAjaxController extends Controller

{

/**

 * Display a listing of the resource.

 *

 * @return \Illuminate\Http\Response

 */

public function index(Request $request)

{



    if ($request-&gt;ajax()) {

        $data = Product::latest()-&gt;get();

        return Datatables::of($data)

                -&gt;addIndexColumn()

                -&gt;addColumn('action', function($row){



                       $btn = '&lt;a href="javascript:void(0)" data-toggle="tooltip"  data-id="'.$row-&gt;id.'" data-original-title="Edit" class="edit btn btn-primary btn-sm editProduct"&gt;Edit&lt;/a&gt;';



                       $btn = $btn.' &lt;a href="javascript:void(0)" data-toggle="tooltip"  data-id="'.$row-&gt;id.'" data-original-title="Delete" class="btn btn-danger btn-sm deleteProduct"&gt;Delete&lt;/a&gt;';



                        return $btn;

                })

                -&gt;rawColumns(['action'])

                -&gt;make(true);

    }

  

    return view('productAjax',compact('products'));

}

 

/**

 * Store a newly created resource in storage.

 *

 * @param  \Illuminate\Http\Request  $request

 * @return \Illuminate\Http\Response

 */

public function store(Request $request)

{

    Product::updateOrCreate(['id' =&gt; $request-&gt;product_id],

            ['name' =&gt; $request-&gt;name, 'detail' =&gt; $request-&gt;detail]);        



    return response()-&gt;json(['success'=&gt;'Product saved successfully.']);

}

/**

 * Show the form for editing the specified resource.

 *

 * @param  \App\Product  $product

 * @return \Illuminate\Http\Response

 */

public function edit($id)

{

    $product = Product::find($id);

    return response()-&gt;json($product);

}



/**

 * Remove the specified resource from storage.

 *

 * @param  \App\Product  $product

 * @return \Illuminate\Http\Response

 */

public function destroy($id)

{

    Product::find($id)-&gt;delete();

 

    return response()-&gt;json(['success'=&gt;'Product deleted successfully.']);

}

}
</pre>

Ok, so after run bellow command you will find “app/Product.php” and put bellow content in Product.php file:

app/Product.php

<pre class="ql-syntax" spellcheck="false"><?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model

{

protected $fillable = [

    'name', 'detail'

];

}
</pre>

Step 7: Create Blade Files

In last step. In this step we have to create just blade file. so we need to create only one blade file as productAjax.blade.php file.

So let’s just create following file and put bellow code.

resources/views/productAjax.blade.php

<pre class="ql-syntax" spellcheck="false"><!DOCTYPE html>

<html>

<head>

&lt;title&gt;Laravel 5.8 Ajax CRUD tutorial using Datatable - ItSolutionStuff.com&lt;/title&gt;

&lt;meta name="csrf-token" content="{{ csrf_token() }}"&gt;

&lt;link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" /&gt;

&lt;link href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" rel="stylesheet"&gt;

&lt;link href="https://cdn.datatables.net/1.10.19/css/dataTables.bootstrap4.min.css" rel="stylesheet"&gt;

&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"&gt;&lt;/script&gt;  

&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.js"&gt;&lt;/script&gt;

&lt;script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"&gt;&lt;/script&gt;

&lt;script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"&gt;&lt;/script&gt;

&lt;script src="https://cdn.datatables.net/1.10.19/js/dataTables.bootstrap4.min.js"&gt;&lt;/script&gt;

</head>

<body>

<div class="container">

&lt;h1&gt;Laravel 5.8 Ajax CRUD tutorial using Datatable - ItSolutionStuff.com&lt;/h1&gt;

&lt;a class="btn btn-success" href="javascript:void(0)" id="createNewProduct"&gt; Create New Product&lt;/a&gt;

&lt;table class="table table-bordered data-table"&gt;

    &lt;thead&gt;

        &lt;tr&gt;

            &lt;th&gt;No&lt;/th&gt;

            &lt;th&gt;Name&lt;/th&gt;

            &lt;th&gt;Details&lt;/th&gt;

            &lt;th width="280px"&gt;Action&lt;/th&gt;

        &lt;/tr&gt;

    &lt;/thead&gt;

    &lt;tbody&gt;

    &lt;/tbody&gt;

&lt;/table&gt;

</div>

<div class="modal fade" id="ajaxModel" aria-hidden="true">

&lt;div class="modal-dialog"&gt;

    &lt;div class="modal-content"&gt;

        &lt;div class="modal-header"&gt;

            &lt;h4 class="modal-title" id="modelHeading"&gt;&lt;/h4&gt;

        &lt;/div&gt;

        &lt;div class="modal-body"&gt;

            &lt;form id="productForm" name="productForm" class="form-horizontal"&gt;

               &lt;input type="hidden" name="product_id" id="product_id"&gt;

                &lt;div class="form-group"&gt;

                    &lt;label for="name" class="col-sm-2 control-label"&gt;Name&lt;/label&gt;

                    &lt;div class="col-sm-12"&gt;

                        &lt;input type="text" class="form-control" id="name" name="name" placeholder="Enter Name" value="" maxlength="50" required=""&gt;

                    &lt;/div&gt;

                &lt;/div&gt;

 

                &lt;div class="form-group"&gt;

                    &lt;label class="col-sm-2 control-label"&gt;Details&lt;/label&gt;

                    &lt;div class="col-sm-12"&gt;

                        &lt;textarea id="detail" name="detail" required="" placeholder="Enter Details" class="form-control"&gt;&lt;/textarea&gt;

                    &lt;/div&gt;

                &lt;/div&gt;

  

                &lt;div class="col-sm-offset-2 col-sm-10"&gt;

                 &lt;button type="submit" class="btn btn-primary" id="saveBtn" value="create"&gt;Save changes

                 &lt;/button&gt;

                &lt;/div&gt;

            &lt;/form&gt;

        &lt;/div&gt;

    &lt;/div&gt;

&lt;/div&gt;

</div>

</body>

<script type="text/javascript">

$(function () {

  $.ajaxSetup({

      headers: {

          'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')

      }

});



var table = $('.data-table').DataTable({

    processing: true,

    serverSide: true,

    ajax: "{{ route('ajaxproducts.index') }}",

    columns: [

        {data: 'DT_RowIndex', name: 'DT_RowIndex'},

        {data: 'name', name: 'name'},

        {data: 'detail', name: 'detail'},

        {data: 'action', name: 'action', orderable: false, searchable: false},

    ]

});

 

$('#createNewProduct').click(function () {

    $('#saveBtn').val("create-product");

    $('#product_id').val('');

    $('#productForm').trigger("reset");

    $('#modelHeading').html("Create New Product");

    $('#ajaxModel').modal('show');

});



$('body').on('click', '.editProduct', function () {

  var product_id = $(this).data('id');

  $.get("{{ route('ajaxproducts.index') }}" +'/' + product_id +'/edit', function (data) {

      $('#modelHeading').html("Edit Product");

      $('#saveBtn').val("edit-user");

      $('#ajaxModel').modal('show');

      $('#product_id').val(data.id);

      $('#name').val(data.name);

      $('#detail').val(data.detail);

  })

});

$('#saveBtn').click(function (e) {

    e.preventDefault();

    $(this).html('Sending..');



    $.ajax({

      data: $('#productForm').serialize(),

      url: "{{ route('ajaxproducts.store') }}",

      type: "POST",

      dataType: 'json',

      success: function (data) {

 

          $('#productForm').trigger("reset");

          $('#ajaxModel').modal('hide');

          table.draw();

     

      },

      error: function (data) {

          console.log('Error:', data);

          $('#saveBtn').html('Save Changes');

      }

  });

});



$('body').on('click', '.deleteProduct', function () {

 

    var product_id = $(this).data("id");

    confirm("Are You sure want to delete !");

  

    $.ajax({

        type: "DELETE",

        url: "{{ route('ajaxproducts.store') }}"+'/'+product_id,

        success: function (data) {

            table.draw();

        },

        error: function (data) {

            console.log('Error:', data);

        }

    });

});

});

</script>

</html>
</pre>

Now you can test it by using following command:

<pre class="ql-syntax" spellcheck="false">php artisan serve
</pre>

Now you can open bellow URL on your browser:

<pre class="ql-syntax" spellcheck="false">http://localhost:8000/ajaxproducts
</pre>

I hope it can help you…


Learn More

PHP with Laravel for beginners - Become a Master in Laravel

Projects in Laravel: Learn Laravel Building 10 Projects

Laravel for RESTful: Build Your RESTful API with Laravel

Fullstack Web Development With Laravel and Vue.js

Developing and Securing GraphQL APIs with Laravel

Build a Basic CRUD App with Laravel and Angular

Build a Basic CRUD App with Laravel and React

Build a Basic CRUD App with Laravel and Vue

Originally published by Hardik Savani at https://itsolutionstuff.com

Securing RESTful API with Spring Boot, Security, and Data MongoDB

Securing RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB. Previously, we have shown you how to securing Spring Boot, MVC and MongoDB web application. In this tutorial, the secure endpoint will restrict the access from an unauthorized request. Every request to secure endpoint should bring authorization token with it. Of course, there will be an endpoint for login which will get authorization token after successful login.


Table of Contents:

The following software, tools, and frameworks are required for this tutorial:

We assume that you already installed all required software, tools, and frameworks. So, we will not cover how to install that software, tools, and frameworks.


1. Generate a New Spring Boot Gradle Project

To create or generate a new Spring Boot Application or Project, simply go to Spring Initializer. Fill all required fields as below then click on Generate Project button.

The project will automatically be downloaded as a Zip file. Next, extract the zipped project to your java projects folder. On the project folder root, you will find build.gradle file for register dependencies, initially it looks like this.

<pre class="ql-syntax" spellcheck="false">buildscript {     ext {         springBootVersion = '2.1.2.RELEASE'     }     repositories {         mavenCentral()     }     dependencies {         classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")     } }

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.djamware'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
}
</pre>

Now, you can work with the source code of this Spring Boot Project using your own IDE or Text Editor. We are using Spring Tool Suite (STS). In STS, import the extracted zipped file as Existing Gradle Project.

Next, we have to add the JWT library to the build.gradle as the dependency. Open and edit build.gradle then add this line to dependencies after other implementation.

<pre class="ql-syntax" spellcheck="false">implementation 'io.jsonwebtoken:jjwt:0.9.1'
</pre>

Next, compile the Gradle Project by type this command from Terminal or CMD.

<pre class="ql-syntax" spellcheck="false">./gradlew compile
</pre>

Or you can compile directly from STS by right-clicking the project name then choose Gradle -> Refresh Gradle Project. Next, open and edit src/main/resources/application.properties then add these lines.

<pre class="ql-syntax" spellcheck="false">spring.data.mongodb.database=springmongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
</pre>

2. Create Product, User and Role Model or Entity Classes

We will be creating all required models or entities for products, user and role. In STS, right-click the project name -> New -> Class. Fill the package with com.djamware.SecurityRest.models, the name with Product, and leave other fields and checkbox as default then click Finish Button.

Next, open and edit src/main/java/com/djamware/SecurityRest/models/Product.java then add this annotation above the class name that will point to MongoDB collection.

<pre class="ql-syntax" spellcheck="false">@Document(collection = "products")
</pre>

Inside Product class, add these variables.

<pre class="ql-syntax" spellcheck="false">@Id
String id;
String prodName;
String prodDesc;
Double prodPrice;
String prodImage;
</pre>

Add constructors after the variable or fields.

<pre class="ql-syntax" spellcheck="false">public Product() {
}

public Product(String prodName, String prodDesc, Double prodPrice, String prodImage) {
    super();
    this.prodName = prodName;
    this.prodDesc = prodDesc;
    this.prodPrice = prodPrice;
    this.prodImage = prodImage;
}
</pre>

Generate or create Getter and Setter for each field.

<pre class="ql-syntax" spellcheck="false">public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getProdName() {
    return prodName;
}

public void setProdName(String prodName) {
    this.prodName = prodName;
}

public String getProdDesc() {
    return prodDesc;
}

public void setProdDesc(String prodDesc) {
    this.prodDesc = prodDesc;
}

public Double getProdPrice() {
    return prodPrice;
}

public void setProdPrice(Double prodPrice) {
    this.prodPrice = prodPrice;
}

public String getProdImage() {
    return prodImage;
}

public void setProdImage(String prodImage) {
    this.prodImage = prodImage;
}
</pre>

Using STS you can organize imports automatically from the menu Source -> Organize Imports then you can see the imports after the package name.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
</pre>

You can do the same way as the above step for User and Role class. Here’s the User class looks like.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.models;

import java.util.Set;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "users")
public class User {

    @Id
    private String id;
    @Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)
    private String email;
    private String password;
    private String fullname;
    private boolean enabled;
    @DBRef
    private Set<Role> roles;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getFullname() {
        return fullname;
    }
    public void setFullname(String fullname) {
        this.fullname = fullname;
    }
    public boolean isEnabled() {
        return enabled;
    }
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
    public Set<Role> getRoles() {
        return roles;
    }
    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

}
</pre>

And the Role class will be like this.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "roles")
public class Role {

    @Id
    private String id;
    @Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)

    private String role;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getRole() {
        return role;
    }
    public void setRole(String role) {
        this.role = role;
    }

}
</pre>

3. Create Product, User and Role Repository Interfaces

Next steps to create Product, User, and Role Repository Interfaces. From the STS, right-click the project name -> New -> Interface then fill all required fields and checkboxes as below before click Finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/repositories/ProductRepository.java then add extends to MongoDB CRUD Repository.

<pre class="ql-syntax" spellcheck="false">public interface ProductRepository extends CrudRepository<Product, String> {

}
</pre>

Inside the class name add this method.

<pre class="ql-syntax" spellcheck="false">@Override
void delete(Product deleted);
</pre>

Organize all required imports.

<pre class="ql-syntax" spellcheck="false">import org.springframework.data.repository.CrudRepository;
import com.djamware.SecurityRest.models.Product;
</pre>

The same way can be applied to User and Role repositories. So, the User Repository Interface will look like this.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.User;

public interface UserRepository extends MongoRepository<User, String> {

    User findByEmail(String email);
}
</pre>

And the Role Repository Interface will look like this.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.Role;

public interface RoleRepository extends MongoRepository<Role, String> {

    Role findByRole(String role);
}
</pre>

4. Create a Custom User Details Service

To implements authentication using existing User and Role from MongoDB, we have to create a custom user details service. From the STS, right-click the project name -> New -> Class File then fill all required fields and checkboxes as below before clicking the finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/services/CustomUserDetailsService.java then give an annotation above the class name and implement the Spring Security User Details Service.

<pre class="ql-syntax" spellcheck="false">@Service
public class CustomUserDetailsService implements UserDetailsService {
}
</pre>

Next, inject all required beans at the first line of the class bracket.

<pre class="ql-syntax" spellcheck="false">@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Autowired
private PasswordEncoder bCryptPasswordEncoder;
</pre>

Add a method to find a user by email field.

<pre class="ql-syntax" spellcheck="false">public User findUserByEmail(String email) {
    return userRepository.findByEmail(email);
}
</pre>

Add a method to save a new user.

<pre class="ql-syntax" spellcheck="false">public void saveUser(User user) {
    user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
    user.setEnabled(true);
    Role userRole = roleRepository.findByRole("ADMIN");
    user.setRoles(new HashSet<>(Arrays.asList(userRole)));
    userRepository.save(user);
}
</pre>

Override the Spring Security User Details to load User by email.

<pre class="ql-syntax" spellcheck="false">@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

    User user = userRepository.findByEmail(email);
    if(user != null) {
        List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
        return buildUserForAuthentication(user, authorities);
    } else {
        throw new UsernameNotFoundException("username not found");
    }
}
</pre>

Add a method to get a set of Roles that related to a user.



<pre class="ql-syntax" spellcheck="false">private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
    Set<GrantedAuthority> roles = new HashSet<>();
    userRoles.forEach((role) -> {
        roles.add(new SimpleGrantedAuthority(role.getRole()));
    });

    List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roles);
    return grantedAuthorities;
}
</pre>

Add a method for authentication purpose.

<pre class="ql-syntax" spellcheck="false">private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
    return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
</pre>

5. Configure Spring Boot Security Rest

Now, the main purpose of this tutorial is configuring Spring Security Rest. First, we have to create a bean for JWT token generation and validation. Right-click the project name -> New -> Class File. Fill the package name as com.djamware.SecurityRest.configs and the Class name as JwtTokenProvider then click the Finish button. Next, open and edit that newly created class file then give it an annotation above the class name.

<pre class="ql-syntax" spellcheck="false">@Component
public class JwtTokenProvider {
}
</pre>

Add variables and injected bean inside the class bracket at the top lines.

<pre class="ql-syntax" spellcheck="false">@Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";

@Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h

@Autowired
private CustomUserDetailsService userDetailsService;
</pre>

Add a method for initialization.

<pre class="ql-syntax" spellcheck="false">@PostConstruct
protected void init() {
    secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
</pre>

Add a method to create a JWT token.

<pre class="ql-syntax" spellcheck="false">public String createToken(String username, Set<Role> set) {
    Claims claims = Jwts.claims().setSubject(username);
    claims.put("roles", set);
    Date now = new Date();
    Date validity = new Date(now.getTime() + validityInMilliseconds);
    return Jwts.builder()//
        .setClaims(claims)//
        .setIssuedAt(now)//
        .setExpiration(validity)//
        .signWith(SignatureAlgorithm.HS256, secretKey)//
        .compact();
}
</pre>

Add a method to load User by username.

<pre class="ql-syntax" spellcheck="false">public Authentication getAuthentication(String token) {
    UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
    return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
</pre>

Add a method to get the username by JWT token.

<pre class="ql-syntax" spellcheck="false">public String getUsername(String token) {
    return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
</pre>

Add a method to resolve JWT token from request headers of Authorization that has a Bearer prefix.

<pre class="ql-syntax" spellcheck="false">public String resolveToken(HttpServletRequest req) {
    String bearerToken = req.getHeader("Authorization");
    if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
        return bearerToken.substring(7, bearerToken.length());
    }
    return null;
}
</pre>

Add a method to validate a JWT token.

<pre class="ql-syntax" spellcheck="false">public boolean validateToken(String token) {
    try {
        Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
        if (claims.getBody().getExpiration().before(new Date())) {
            return false;
        }
        return true;
    } catch (JwtException | IllegalArgumentException e) {
        throw new JwtException("Expired or invalid JWT token");
    }
}
</pre>

Finally, organize imports like below.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.configs;

import java.util.Base64;
import java.util.Date;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.djamware.SecurityRest.models.Role;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
</pre>

Next, create a JWT filter class with the name JwtTokenFilter in configs package that extends Spring GenericFilterBean. Replace all Java codes with these lines of codes.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.configs;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;

public class JwtTokenFilter extends GenericFilterBean {

    private JwtTokenProvider jwtTokenProvider;

    public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
        throws IOException, ServletException {
        String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
        if (token != null && jwtTokenProvider.validateToken(token)) {
            Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        filterChain.doFilter(req, res);
    }
}
</pre>

Next, create a class with the name JwtConfigurer for JWT configuration in configs package then replace all codes with these lines of codes.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.configs;

import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    private JwtTokenProvider jwtTokenProvider;

    public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
        http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
    }
}
</pre>

Finally, we have to configure the Spring Security by creating a Java class file inside configs package with the name WebSecurityConfig. Give annotations to this class and extends Spring WebSecurityConfigurerAdapter.

<pre class="ql-syntax" spellcheck="false">@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
</pre>

Inject JWT token provider inside this class.

<pre class="ql-syntax" spellcheck="false">@Autowired
JwtTokenProvider jwtTokenProvider;
</pre>

Add an override method to configure Authentication Manager Builder.

<pre class="ql-syntax" spellcheck="false">@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    UserDetailsService userDetailsService = mongoUserDetails();
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());

}
</pre>

Next, add an override method to configure Spring Security Http Security.

<pre class="ql-syntax" spellcheck="false">@Override
protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic().disable().csrf().disable().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
            .antMatchers("/api/auth/login").permitAll().antMatchers("/api/auth/register").permitAll()
            .antMatchers("/api/products/**").hasAuthority("ADMIN").anyRequest().authenticated().and().csrf()
            .disable().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint()).and()
            .apply(new JwtConfigurer(jwtTokenProvider));
}
</pre>

Next, declare all required beans for this configuration.

<pre class="ql-syntax" spellcheck="false">@Bean
public PasswordEncoder bCryptPasswordEncoder() {
    return new BCryptPasswordEncoder();
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}

@Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
    return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
            "Unauthorized");
}

@Bean
public UserDetailsService mongoUserDetails() {
    return new CustomUserDetailsService();
}
</pre>

6. Create Product and Authentication Controllers

Now it’s time for REST API endpoint. All RESTful API will be created from each controller. Product controller will handle CRUD endpoint of product and Authentication controller will handle login and register endpoint. Right-click project name -> New -> Class then fill the package with com.djamware.SecurityRest.controllers and the class name as ProductController. Open and edit the newly created class file then replace all codes with these lines of codes.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.controllers;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.models.Product;
import com.djamware.SecurityRest.repositories.ProductRepository;

@RestController
public class ProductController {

    @Autowired
    ProductRepository productRepository;

    @RequestMapping(method=RequestMethod.GET, value="/api/products")
    public Iterable<Product> product() {
        return productRepository.findAll();
    }

    @RequestMapping(method=RequestMethod.POST, value="/api/products")
    public String save(@RequestBody Product product) {
        productRepository.save(product);

        return product.getId();
    }

    @RequestMapping(method=RequestMethod.GET, value="/api/products/{id}")
    public Optional<Product> show(@PathVariable String id) {
        return productRepository.findById(id);
    }

    @RequestMapping(method=RequestMethod.PUT, value="/api/products/{id}")
    public Product update(@PathVariable String id, @RequestBody Product product) {
        Optional<Product> prod = productRepository.findById(id);
        if(product.getProdName() != null)
            prod.get().setProdName(product.getProdName());
        if(product.getProdDesc() != null)
            prod.get().setProdDesc(product.getProdDesc());
        if(product.getProdPrice() != null)
            prod.get().setProdPrice(product.getProdPrice());
        if(product.getProdImage() != null)
            prod.get().setProdImage(product.getProdImage());
        productRepository.save(prod.get());
        return prod.get();
    }

    @RequestMapping(method=RequestMethod.DELETE, value="/api/products/{id}")
    public String delete(@PathVariable String id) {
        Optional<Product> product = productRepository.findById(id);
        productRepository.delete(product.get());

        return "product deleted";
    }
}
</pre>

For login, we need to create a POJO to mapping required fields of User. Create a new class file with the name AuthBody inside controllers package then replace all Java codes with these lines of codes.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.controllers;

public class AuthBody {

    private String email;
    private String password;

    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }

}
</pre>

Finally, create a controller for authentication with the name AuthController inside the controllers’ package. Open and edit that newly created file then replace all Java codes with these lines of codes.

<pre class="ql-syntax" spellcheck="false">package com.djamware.SecurityRest.controllers;

import static org.springframework.http.ResponseEntity.ok;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.configs.JwtTokenProvider;
import com.djamware.SecurityRest.models.User;
import com.djamware.SecurityRest.repositories.UserRepository;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    JwtTokenProvider jwtTokenProvider;

    @Autowired
    UserRepository users;

    @Autowired
    private CustomUserDetailsService userService;

    @SuppressWarnings("rawtypes")
    @PostMapping("/login")
    public ResponseEntity login(@RequestBody AuthBody data) {
        try {
            String username = data.getEmail();
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, data.getPassword()));
            String token = jwtTokenProvider.createToken(username, this.users.findByEmail(username).getRoles());
            Map<Object, Object> model = new HashMap<>();
            model.put("username", username);
            model.put("token", token);
            return ok(model);
        } catch (AuthenticationException e) {
            throw new BadCredentialsException("Invalid email/password supplied");
        }
    }

    @SuppressWarnings("rawtypes")
    @PostMapping("/register")
    public ResponseEntity register(@RequestBody User user) {
        User userExists = userService.findUserByEmail(user.getEmail());
        if (userExists != null) {
            throw new BadCredentialsException("User with username: " + user.getEmail() + " already exists");
        }
        userService.saveUser(user);
        Map<Object, Object> model = new HashMap<>();
        model.put("message", "User registered successfully");
        return ok(model);
    }
}
</pre>

7. Run and Test Spring Boot Security Rest using Postman

Before run and test the application, we have to populate a Role data first. Open and edit src/main/java/com/djamware/SecurityRest/SecurityRestApplication.java then add these lines of codes inside the initialization method.



<pre class="ql-syntax" spellcheck="false">@Bean
CommandLineRunner init(RoleRepository roleRepository) {

    return args -> {

        Role adminRole = roleRepository.findByRole("ADMIN");
        if (adminRole == null) {
            Role newAdminRole = new Role();
            newAdminRole.setRole("ADMIN");
            roleRepository.save(newAdminRole);
        }
    };

}
</pre>

Next, make sure you have run the MongoDB server on your local machine then run the Gradle application using this command.

<pre class="ql-syntax" spellcheck="false">./gradlew bootRun
</pre>

Or in STS just right-click the project name -> Run As -> Spring Boot App. Next, open the Postman application then change the method to GET and address to localhost:8080/api/products then click Send button.

You will see this response in the bottom panel of Postman.

<pre class="ql-syntax" spellcheck="false">{
    "timestamp": "2019-03-07T13:16:34.935+0000",
    "status": 401,
    "error": "Unauthorized",
    "message": "Unauthorized",
    "path": "/api/products"
}
</pre>

Next, change the method to POST then address to localhost:8080/api/auth/register then fill the body with raw data as below image then click Send button.

You will get the response in the bottom panel of Postman.

<pre class="ql-syntax" spellcheck="false">{
    "message": "User registered successfully"
}
</pre>

Next, change the address to localhost:8080/api/auth/login and change the body as below then click Send button.

<pre class="ql-syntax" spellcheck="false">{ "email":"[email protected]", "password": "q1w2we3r4" }
</pre>

You will see this response in the bottom panel of Postman.

<pre class="ql-syntax" spellcheck="false">{
    "username": "[email protected]",
    "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo"
}
</pre>

Copy the token then back to the GET product. Add a header with the name Authorization and the value that paste from a token that gets by login with additional Bearer prefix (with space) as below.

<pre class="ql-syntax" spellcheck="false">Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo
</pre>

You should see this response after clicking the Send button.

<pre class="ql-syntax" spellcheck="false">[
    {
        "id": "5c80dc6cb20191520567b68a",
        "prodName": "Dummy Product 1",
        "prodDesc": "The Fresh Dummy Product in The world part 1",
        "prodPrice": 100,
        "prodImage": "https://dummyimage.com/600x400/000/fff"
    }
]
</pre>

You can test the POST product with the token in headers using the same way.

That it’s, the Securing RESTful API with Spring Boot, Security, and Data MongoDB tutorial. You can get the full source code from our GitHub.


Learn More

Build a Simple CRUD App with Spring Boot and Vue.js

Creating RESTful APIs with NodeJS and MongoDB Tutorial

MongoDB with Python Crash Course - Tutorial for Beginners

How to build RESTful APIs with ASP.NET Core

Understanding the basics of RESTful APIs

Developing RESTful APIs with Lumen (A PHP Micro-framework)

Java Programming Masterclass for Software Developers

Java In-Depth: Become a Complete Java Engineer!

JSP, Servlets and JDBC for Beginners: Build a Database App

JSP, Servlet, JSLT + Hibernate: A complete guide

Originally published by Didin J. on at https://www.djamware.com/

Building a full CRUD Angular 8 Universal and MongoDB SSR

Building a full CRUD Angular 8 Universal and MongoDB SSR

We will practically use Angular 8 universal for a CRUD (Create, Read, Update, Delete) operation application that store the data to the MongoDB server.

The Angular 8 application that we will build is a very simple application about Article CRUD. This might be a starting point to build your own complete blog engine or news engine because this application should be matched for SEO requirements.

Almost all codes in this application using Typescript and ES6 except a few files that still use plain ES5 code that uses by Express.js. For that, we need your suggestion for converting to working Angular 8/Typescript code styles. The following tools, frameworks, and modules are required for this tutorial:

As usual, before we move forward to the main steps of Angular 8 Universal and MongoDB Server-side Rendering (SSR). We have to check the latest Node and Angular versions then update them if necessary. Just type this command to see it.

<pre class="ql-syntax" spellcheck="false">ng version </pre>

You will see the currently installed Node, Angular and Angular CLI versions.

Angular CLI: 8.0.6
Node: 10.15.1
OS: darwin x64
Angular: 8.0.3

Create a New Angular 8 Application and Add Angular Universal

First, we will create a new Angular 8 application by type this command in the terminal.

<pre class="ql-syntax" spellcheck="false">ng new article-crud </pre>

Next, go to the newly created Angular 8 application folder then type this command to add Angular Universal SSR.

<pre class="ql-syntax" spellcheck="false">ng add @nguniversal/express-engine --clientProject article-crud </pre>

That command just added and updated a few files.

CREATE src/main.server.ts (361 bytes)
CREATE src/app/app.server.module.ts (427 bytes)
CREATE tsconfig.server.json (204 bytes)
CREATE webpack.server.config.js (1466 bytes)
CREATE server.ts (1980 bytes)
UPDATE package.json (1844 bytes)
UPDATE angular.json (4263 bytes)
UPDATE src/main.ts (432 bytes)
UPDATE src/app/app.module.ts (438 bytes)

Next, check the Angular Universal SSR application by running this application using these commands.

<pre class="ql-syntax" spellcheck="false">npm run build:ssr && npm run serve:ssr </pre>

Now, after go to localhost:4000 you see this standard Angular application page and feel the difference with the page that rendered in the browser.

Angular 8 Universal

Install and Configure Mongoose.js and Required Modules

To store and retrieve data from the MongoDB server, we will use Mongoose.js. To install it, just run these commands that including body-parser modules.

<pre class="ql-syntax" spellcheck="false">npm install --save mongoose body-parser </pre>

In this Angular 8 Universal SSR, body parser will use to parse the request body to the API. Next, open and edit server.ts in the root project folder then add these imports.

import * as mongoose from 'mongoose';
import bodyParser from "body-parser";

Next, add these lines of codes after the imports lines to connect to the MongoDB and use body-parser for every JSON requests.

mongoose.connect('mongodb://localhost/angular8-crud', { useNewUrlParser: true, useFindAndModify: false })
  .then(() =>  console.log('connection successful'))
  .catch((err) => console.error(err));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

Create a Mongoose Model and Router

We will create a Mongoose model that represent MongoDB fields. Create a models folder and a file to holds all required Article fields.

mkdir models
touch models/article.ts

Next, fill that file with this Mongoose Schema.

import mongoose, { Schema } from 'mongoose';

const ArticleSchema: Schema = new Schema({
  title: { type: String, required: true },
  author: { type: String, required: true },
  description: { type: String, required: true },
  content: { type: String, required: true },
  updatedAt: { type: Date, default: Date.now }
});

export default mongoose.model('Article', ArticleSchema);

Next, we will create a route that accesses the MongoDB data via API. Create a new folder call routes and a file inside it.

mkdir routes
touch routes/article-route.ts

Open and edit that file then add these imports.

import { Request, Response, NextFunction } from 'express';
import Article from '../models/article';

Add these lines of codes to create a CRUD method of RESTful API. The

route path starts with /api/ that will be called from the Angular 8

application using full URL [http://localhost:4000/api/.](http://localhost:4000/api/`.)

export class ArticleRoute {

  public articleRoute(app): void {
    app.route('/api/').get((req: Request, res: Response, next: NextFunction) => {
      Article.find((err, articles) => {
        if (err) { return next(err); }
        res.json(articles);
      });
    });

    app.route('/api/:id').get((req: Request, res: Response, next: NextFunction) => {
      Article.findById(req.params.id, (err, article) => {
        if (err) { return next(err); }
        res.json(article);
      });
    });

    app.route('/api/').post((req: Request, res: Response, next: NextFunction) => {
      console.log(req.body);
      Article.create(req.body, (err, article) => {
        if (err) { return next(err); }
        res.json(article);
      });
    });

    app.route('/api/:id').put((req: Request, res: Response, next: NextFunction) => {
      Article.findByIdAndUpdate(req.params.id, req.body, (err, article) => {
        if (err) { return next(err); }
        res.json(article);
      });
    });

    app.route('/api/:id').delete((req: Request, res: Response, next: NextFunction) => {
      Article.findByIdAndRemove(req.params.id, req.body, (err, article) => {
        if (err) { return next(err); }
        res.json(article);
      });
    });
  }
}

Next, open and edit server.ts then add this import.

<pre class="ql-syntax" spellcheck="false">import { ArticleRoute } from './routes/article-route'; </pre>

Declare a constant after the import lines.

<pre class="ql-syntax" spellcheck="false">const articleRoute: ArticleRoute = new ArticleRoute(); </pre>

Add this line before the all regular route lines.

<pre class="ql-syntax" spellcheck="false">articleRoute.articleRoute(app); </pre>

Don't declare API route after all regular route otherwise the API URL won't working.

Test the Angular 8 Universal SSR RESTful API

We will test the Angular 8 Universal SSR RESTful API using the Postman app. First, run the app using this command.

<pre class="ql-syntax" spellcheck="false">npm run build:ssr && npm run serve:ssr </pre>

Open the postman app then change the method to GET and address to localhost:4000/api/ then press SEND button. If there's any data in your MongoDB server then it's will be similar like this.

MongoDB SSR

To save data, change the method to POST with the same address then choose RAW body with type JSON. Fill the RAW body with this example or your own data example.

{
    "title": "Odong",
    "author": "Odong",
    "description": "Odong",
    "content": "Odong"
}

You will see this result for successful POST data.

CRUD Angular 8

Next, you can test the rest of PUT and DELETE using your own data to make sure the Angular 8 Universal SSR API working.

Create Angular 8 Routes for Page Navigation

To create Angular 8 Routes for navigation between Angular 8 pages/component, add or generate all required component.

ng g component articles --skip-import
ng g component show-article --skip-import
ng g component add-article --skip-import
ng g component edit-article --skip-import

There's an additional --skip-import variable because there will be a conflict between 2 modules, Angular 8 and Server modules.

<pre class="ql-syntax" spellcheck="false">More than one module matches. Use skip-import option to skip importing the component into the closest module. </pre>

Next, we will manually add or register those components to the src/app/app.module.ts. Add these imports to that file.

import { ArticlesComponent } from './articles/articles.component';
import { ShowArticleComponent } from './show-article/show-article.component';
import { AddArticleComponent } from './add-article/add-article.component';
import { EditArticleComponent } from './edit-article/edit-article.component';

Then add those components to @NgModule declaration.

@NgModule({
  declarations: [
    ...
    ArticlesComponent,
    ShowArticleComponent,
    AddArticleComponent,
    EditArticleComponent
  ],
  ...
})

Next, open and edit src/app/app-routing.module.ts then add these imports.

const routes: Routes = [
  {
    path: 'articles',
    component: ArticlesComponent,
    data: { title: 'Articles' }
  },
  {
    path: 'show-article/:id',
    component: ShowArticleComponent,
    data: { title: 'Show Article' }
  },
  {
    path: 'add-article',
    component: AddArticleComponent,
    data: { title: 'Add Article' }
  },
  {
    path: 'edit-article/:id',
    component: EditArticleComponent,
    data: { title: 'Edit Article' }
  },
  { path: '',
    redirectTo: '/articles',
    pathMatch: 'full'
  }
];

Open and edit src/app/app.component.html and you will see the existing router outlet. Next, modify this HTML page to fit the CRUD page.

<div class="container">
  <router-outlet></router-outlet>
</div>

Open and edit src/app/app.component.scss then replace all SASS codes with this.

.container {
  padding: 20px;
}

Create an Angular 8 Service using HttpClient, Observable and RXJS

To access RESTful API from Angular 8, we need to create an Angular 8 service which will handle all POST, GET, UPDATE, DELETE requests. The response from the RESTful API emitted by Observable that can subscribe and read from the Components. For error handler and data Extraction, we will use RXJS Library. Before creating a service for RESTful API access, first, we have to install or register HttpClientModule. Open and edit src/app/app.module.ts then add this import.

import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

Add it to @NgModule imports after BrowserModule.

imports: [
  BrowserModule,
  FormsModule,
  HttpClientModule,
  AppRoutingModule
],

We will use type specifier to get a typed result object. For that, create a new Typescript file src/app/article.ts then add these lines of Typescript codes.

export class Article {
  _id: string;
  title: string;
  author: string;
  description: string;
  content: string;
  updatedAt: Date;
}

Next, generate an Angular 8 service by typing this command.

<pre class="ql-syntax" spellcheck="false">ng g service api </pre>

Next, open and edit src/app/api.service.ts then add these imports.

import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { catchError, tap, map } from 'rxjs/operators';
import { Article } from './article';

Add these constants before the @Injectable.

const httpOptions = {
  headers: new HttpHeaders({'Content-Type': 'application/json'})
};
const apiUrl = "/api/";

Inject HttpClient module to the constructor.

<pre class="ql-syntax" spellcheck="false">constructor(private http: HttpClient) { } </pre>

Add the error handler function.

private handleError<T>(operation = 'operation', result?: T) {
  return (error: any): Observable<T> => {
    console.error(error); // log to console instead
    return of(result as T);
  };
}

Add the functions for all CRUD (create, read, update, delete) RESTful call of article data.

getArticles(): Observable<Article[]> {
  return this.http.get<Article[]>(apiUrl)
    .pipe(
      tap(articles => console.log('fetched Articles')),
      catchError(this.handleError('getArticles', []))
    );
}

getArticle(id: number): Observable<Article> {
  const url = `${apiUrl}/${id}`;
  return this.http.get<Article>(url).pipe(
    tap(_ => console.log(`fetched Article id=${id}`)),
    catchError(this.handleError<Article>(`getArticle id=${id}`))
  );
}

addArticle(article: Article): Observable<Article> {
  return this.http.post<Article>(apiUrl, article, httpOptions).pipe(
    tap((art: Article) => console.log(`added Article w/ id=${art._id}`)),
    catchError(this.handleError<Article>('addArticle'))
  );
}

updateArticle(id: any, article: Article): Observable<any> {
  const url = `${apiUrl}/${id}`;
  return this.http.put(url, article, httpOptions).pipe(
    tap(_ => console.log(`updated Article id=${id}`)),
    catchError(this.handleError<any>('updateArticle'))
  );
}

deleteArticle(id: any): Observable<Article> {
  const url = `${apiUrl}/${id}`;
  return this.http.delete<Article>(url, httpOptions).pipe(
    tap(_ => console.log(`deleted Article id=${id}`)),
    catchError(this.handleError<Article>('deleteArticle'))
  );
}

You can find more details about Angular 8 Observable and RXJS here.

Display List of Articles using Angular 8 Material

We will display the list of articles published from API Service. The data published from the API service read by subscribing as an Article model in the Angular 8 component. For that, open and edit src/app/articles/articles.component.ts then add these imports.

<pre class="ql-syntax" spellcheck="false">import { ApiService } from '../api.service'; </pre>

Next, inject the API Service to the constructor.

<pre class="ql-syntax" spellcheck="false">constructor(private api: ApiService) { } </pre>

Next, for the user interface (UI) we will use Angular 8 Material and CDK. There's a CLI for generating a Material component like Table as a component, but we will create or add the Table component from scratch to existing component. Type this command to install Angular 8 Material.

<pre class="ql-syntax" spellcheck="false">ng add @angular/material </pre>

If there are questions like below, just use the default answer.

? Choose a prebuilt theme name, or "custom" for a custom theme: Purple/Green       [ Preview: h
ttps://material.angular.io?theme=purple-green ]
? Set up HammerJS for gesture recognition? Yes
? Set up browser animations for Angular Material? Yes

We will register all required Angular 8 Material components or modules to src/app/app.module.ts. Open and edit that file then add these imports.

import {
  MatInputModule,
  MatPaginatorModule,
  MatProgressSpinnerModule,
  MatSortModule,
  MatTableModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule } from "@angular/material";

Also, modify FormsModule import to add ReactiveFormsModule.

<pre class="ql-syntax" spellcheck="false">import { FormsModule, ReactiveFormsModule } from '@angular/forms'; </pre>

Register the above modules to @NgModule imports.

imports: [
  BrowserModule,
  FormsModule,
  HttpClientModule,
  AppRoutingModule,
  ReactiveFormsModule,
  BrowserAnimationsModule,
  MatInputModule,
  MatTableModule,
  MatPaginatorModule,
  MatSortModule,
  MatProgressSpinnerModule,
  MatIconModule,
  MatButtonModule,
  MatCardModule,
  MatFormFieldModule
],

Next, back to src/app/articles/articles.component.ts then add these imports.

<pre class="ql-syntax" spellcheck="false">import { Article } from '../article'; </pre>

Declare the variables of Angular 8 Material Table Data Source before the constructor.

displayedColumns: string[] = ['title', 'author'];
data: Article[] = [];
isLoadingResults = true;

Modify the ngOnInit function to get list of articles immediately.

ngOnInit() {
  this.api.getArticles()
    .subscribe((res: any) => {
      this.data = res;
      console.log(this.data);
      this.isLoadingResults = false;
    }, err => {
      console.log(err);
      this.isLoadingResults = false;
    });
}

Next, open and edit src/app/articles/articles.component.html then replace all HTML tags with this Angular Material tags.

<div class="example-container mat-elevation-z8">
  <div class="example-loading-shade"
       *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <a mat-flat-button color="primary" [routerLink]="['/add-article']"><mat-icon>add</mat-icon></a>
  </div>
  <div class="mat-elevation-z8">
    <table mat-table [dataSource]="data" class="example-table"
           matSort matSortActive="title" matSortDisableClear matSortDirection="asc">

      <!-- Article Title Column -->
      <ng-container matColumnDef="title">
        <th mat-header-cell *matHeaderCellDef>Title</th>
        <td mat-cell *matCellDef="let row">{{row.title}}</td>
      </ng-container>

      <!-- Article Author Column -->
      <ng-container matColumnDef="author">
        <th mat-header-cell *matHeaderCellDef>Author</th>
        <td mat-cell *matCellDef="let row">$ {{row.author}}</td>
      </ng-container>

      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="['/show-article/', row._id]"></tr>
    </table>
  </div>
</div>

Finally, to make a little UI adjustment, open and edit src/app/articles/articles.component.scss then add this CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-table-container {
  position: relative;
  max-height: 400px;
  overflow: auto;
}

table {
  width: 100%;
}

.example-loading-shade {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 56px;
  right: 0;
  background: rgba(0, 0, 0, 0.15);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.example-rate-limit-reached {
  color: #980000;
  max-width: 360px;
  text-align: center;
}

/* Column Widths */
.mat-column-number,
.mat-column-state {
  max-width: 64px;
}

.mat-column-created {
  max-width: 124px;
}

.mat-flat-button {
  margin: 5px;
}

Show and Delete an Article Details using Angular 8 Material

To show article details after click or tap on the one of a row inside the Angular 8 Material table, open and edit src/app/show-article/show-article.component.ts then add these imports.

import { ActivatedRoute, Router } from '@angular/router';
import { ApiService } from '../api.service';
import { Article } from '../article';

Inject above modules to the constructor.

<pre class="ql-syntax" spellcheck="false">constructor(private route: ActivatedRoute, private api: ApiService, private router: Router) { } </pre>

Declare the variables before the constructor for hold article data that get from the API.

article: Article = { _id: '', title: '', author: '', description: '', content: '', updatedAt: null };
isLoadingResults = true;

Add a function for getting Article data from the API.

getArticleDetails(id: any) {
  this.api.getArticle(id)
    .subscribe((data: any) => {
      this.article = data;
      console.log(this.article);
      this.isLoadingResults = false;
    });
}

Call that function when the component is initiated.

ngOnInit() {
  this.getArticleDetails(this.route.snapshot.params.id);
}

Add this function for delete article.

deleteArticle(id: any) {
  this.isLoadingResults = true;
  this.api.deleteArticle(id)
    .subscribe(res => {
        this.isLoadingResults = false;
        this.router.navigate(['/articles']);
      }, (err) => {
        console.log(err);
        this.isLoadingResults = false;
      }
    );
}

For the view, open and edit src/app/article-detail/article-detail.component.html then replace all HTML tags with this.

<div class="example-container mat-elevation-z8">
  <div class="example-loading-shade"
       *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <a mat-flat-button color="primary" [routerLink]="['/articles']"><mat-icon>list</mat-icon></a>
  </div>
  <mat-card class="example-card">
    <mat-card-header>
      <mat-card-title><h2>{{article.title}}</h2></mat-card-title>
      <mat-card-subtitle>{{article.author}} | {{article.updatedAt}}</mat-card-subtitle>
    </mat-card-header>
    <mat-card-content>
      <h3>{{article.description}}</h3>
      <p>{{article.content}}</p>
    </mat-card-content>
    <mat-card-actions>
      <a mat-flat-button color="primary" [routerLink]="['/edit-article', article._id]"><mat-icon>edit</mat-icon></a>
      <a mat-flat-button color="warn" (click)="deleteArticle(article._id)"><mat-icon>delete</mat-icon></a>
    </mat-card-actions>
  </mat-card>
</div>

Finally, open and edit src/app/article-detail/article-detail.component.scss then add this lines of CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-loading-shade {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 56px;
  right: 0;
  background: rgba(0, 0, 0, 0.15);
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.mat-flat-button {
  margin: 5px;
}

Add an Article using Angular 8 Material

To create a form for adding a Article, open and edit src/app/add-article/add-article.component.ts then add these imports.

import { Router } from '@angular/router';
import { ApiService } from '../api.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';

Inject above modules to the constructor.

<pre class="ql-syntax" spellcheck="false">constructor(private router: Router, private api: ApiService, private formBuilder: FormBuilder) { } </pre>

Declare variables for the Form Group and all of the required fields inside the form before the constructor.

articleForm: FormGroup;
title = '';
author = '';
description = '';
content = '';
isLoadingResults = false;

Add initial validation for each field.

ngOnInit() {
  this.articleForm = this.formBuilder.group({
    'title' : [null, Validators.required],
    'author' : [null, Validators.required],
    'description' : [null, Validators.required],
    'content' : [null, Validators.required]
  });
}

Create a function for submitting or POST article form.

onFormSubmit() {
  this.isLoadingResults = true;
  this.api.addArticle(this.articleForm.value)
    .subscribe((res: any) => {
        const id = res._id;
        this.isLoadingResults = false;
        this.router.navigate(['/show-article', id]);
      }, (err: any) => {
        console.log(err);
        this.isLoadingResults = false;
      });
}

Next, add this import for implementing ErrorStateMatcher.

<pre class="ql-syntax" spellcheck="false">import { ErrorStateMatcher } from '@angular/material/core'; </pre>

Create a new class before the main class @Components.

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Instantiate that MyErrorStateMatcher as a variable in main class.

<pre class="ql-syntax" spellcheck="false">matcher = new MyErrorStateMatcher(); </pre>

Next, open and edit src/app/add-article/add-article.component.html then replace all HTML tags with this.

<div class="example-container mat-elevation-z8">
  <div class="example-loading-shade"
       *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <a mat-flat-button color="primary" [routerLink]="['/articles']"><mat-icon>list</mat-icon></a>
  </div>
  <mat-card class="example-card">
    <form [formGroup]="articleForm" (ngSubmit)="onFormSubmit()">
      <mat-form-field class="example-full-width">
        <input matInput placeholder="Title" formControlName="title"
               [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!articleForm.get('title').valid && articleForm.get('title').touched">Please enter Title</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <input matInput placeholder="Author" formControlName="author"
               [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!articleForm.get('author').valid && articleForm.get('author').touched">Please enter Author</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <input matInput placeholder="Description" formControlName="description"
               [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!articleForm.get('description').valid && articleForm.get('description').touched">Please enter Description</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <textarea matInput placeholder="Content" formControlName="content"
               [errorStateMatcher]="matcher"></textarea>
        <mat-error>
          <span *ngIf="!articleForm.get('content').valid && articleForm.get('content').touched">Please enter Content</span>
        </mat-error>
      </mat-form-field>
      <div class="button-row">
        <button type="submit" [disabled]="!articleForm.valid" mat-flat-button color="primary"><mat-icon>save</mat-icon></button>
      </div>
    </form>
  </mat-card>
</div>

Finally, open and edit src/app/article-add/article-add.component.scss then add this CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child(0) {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}

.mat-flat-button {
  margin: 5px;
}

Edit an Article using Angular 8 Material

We have put an edit button inside the Article Detail component for call Edit page. Now, open and edit src/app/edit-article/edit-article.component.ts then add these imports.

import { Router, ActivatedRoute } from '@angular/router';
import { ApiService } from '../api.service';
import { FormControl, FormGroupDirective, FormBuilder, FormGroup, NgForm, Validators } from '@angular/forms';

Inject above modules to the constructor.

constructor(private router: Router, private route: ActivatedRoute, private api: ApiService, private formBuilder: FormBuilder) { }

Declare the Form Group variable and all of the required variables for the article form before the constructor.

articleForm: FormGroup;
_id = '';
title = '';
author = '';
description = '';
content = '';
isLoadingResults = false;

Next, add validation for all fields when the component is initiated.

ngOnInit() {
  this.getArticle(this.route.snapshot.params.id);
  this.articleForm = this.formBuilder.group({
    'title' : [null, Validators.required],
    'author' : [null, Validators.required],
    'description' : [null, Validators.required],
    'content' : [null, Validators.required]
  });
}

Create a function for getting article data that filled to each form fields.

getArticle(id: any) {
  this.api.getArticle(id).subscribe((data: any) => {
    this._id = data._id;
    this.articleForm.setValue({
      title: data.title,
      author: data.author,
      description: data.description,
      content: data.content
    });
  });
}

Create a function to update the article changes.

onFormSubmit() {
  this.isLoadingResults = true;
  this.api.updateArticle(this._id, this.articleForm.value)
    .subscribe((res: any) => {
        const id = res._id;
        this.isLoadingResults = false;
        this.router.navigate(['/show-article', id]);
      }, (err: any) => {
        console.log(err);
        this.isLoadingResults = false;
      }
    );
}

Add a function for handling the show article details button.

articleDetails() {
  this.router.navigate(['/show-article', this._id]);
}

Next, add this import for implementing ErrorStateMatcher.

<pre class="ql-syntax" spellcheck="false">import { ErrorStateMatcher } from '@angular/material/core'; </pre>

Create a new class before the main class @Components.

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const isSubmitted = form && form.submitted;
    return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
  }
}

Instantiate that MyErrorStateMatcher as a variable in main class.

<pre class="ql-syntax" spellcheck="false">matcher = new MyErrorStateMatcher(); </pre>

Next, open and edit src/app/edit-article/edit-article.component.html then replace all HTML tags with this.

<div class="example-container mat-elevation-z8">
  <div class="example-loading-shade"
       *ngIf="isLoadingResults">
    <mat-spinner *ngIf="isLoadingResults"></mat-spinner>
  </div>
  <div class="button-row">
    <a mat-flat-button color="primary" (click)="articleDetails()"><mat-icon>info</mat-icon></a>
  </div>
  <mat-card class="example-card">
    <form [formGroup]="articleForm" (ngSubmit)="onFormSubmit()">
      <mat-form-field class="example-full-width">
        <input matInput placeholder="Title" formControlName="title"
               [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!articleForm.get('title').valid && articleForm.get('title').touched">Please enter Title</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <input matInput placeholder="Author" formControlName="author"
               [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!articleForm.get('author').valid && articleForm.get('author').touched">Please enter Author</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <input matInput placeholder="Description" formControlName="description"
               [errorStateMatcher]="matcher">
        <mat-error>
          <span *ngIf="!articleForm.get('description').valid && articleForm.get('description').touched">Please enter Description</span>
        </mat-error>
      </mat-form-field>
      <mat-form-field class="example-full-width">
        <textarea matInput placeholder="Content" formControlName="content"
               [errorStateMatcher]="matcher"></textarea>
        <mat-error>
          <span *ngIf="!articleForm.get('content').valid && articleForm.get('content').touched">Please enter Content</span>
        </mat-error>
      </mat-form-field>
      <div class="button-row">
        <button type="submit" [disabled]="!articleForm.valid" mat-flat-button color="primary"><mat-icon>save</mat-icon></button>
      </div>
    </form>
  </mat-card>
</div>

Finally, open and edit src/app/edit-article/edit-article.component.scss then add this lines of CSS codes.

/* Structure */
.example-container {
  position: relative;
  padding: 5px;
}

.example-form {
  min-width: 150px;
  max-width: 500px;
  width: 100%;
}

.example-full-width {
  width: 100%;
}

.example-full-width:nth-last-child(0) {
  margin-bottom: 10px;
}

.button-row {
  margin: 10px 0;
}

.mat-flat-button {
  margin: 5px;
}

Test and Run Angular 8 Universal and MongoDB Server-side Rendering (SSR)

It's time to test and try the performance of Angular 8 Universal and MongoDB Server-side Rendering (SSR). First, run the MongoDB server in the different terminal or command line.

<pre class="ql-syntax" spellcheck="false">mongod </pre>

Next, run the Angular 8 Universal SSR in the current terminal and project folder.

<pre class="ql-syntax" spellcheck="false">npm run build:ssr && npm run serve:ssr </pre>

After build finished for both client and server then open the

application in the browser [http://localhost:4200/](http://localhost:4200/`) and you will see and

feel this Angular 8 application as below.

That it's, the example of Angular 8 Universal and MongoDB Server-side Rendering (SSR). You can get the full source codes from our Github.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading about AngularJS

Angular 8 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Complete Node.js Developer Course (3rd Edition)

The Web Developer Bootcamp

Best 50 Angular Interview Questions for Frontend Developers in 2019

MEAN Stack Angular 8 CRUD Web Application

Angular 8 Tutorial - User Registration and Login Example

How to build a CRUD Web App with Angular 8.0

MEAN Stack Tutorial MongoDB, ExpressJS, AngularJS and NodeJS

AngularJS tutorial for beginners with NodeJS, ExpressJS and MongoDB

Node.js With MongoDB Authentication

Fullstack Vue App with MongoDB, Express.js and Node.js

MERN Stack Tutorial - Build a MERN App From Scratch ❤

File Upload with Vue, Apollo Client and GraphQL

File Upload with Vue, Apollo Client and GraphQL

The tutorial will be divided into two main sections: building the GraphQL API and the frontend app. The GraphQL API will be built using Apollo Server and the frontend app will be built with Vue.js and Vue Apollo.

Table of Contents

  • Prerequisites
  • What we'll be building
  • Getting your Cloudinary keys
  • Building the GraphQL server
  • Building the frontend app
  • Fetching all photos
  • Uploading photo
  • Conclusion

Prerequisites

This tutorial assumes you conformatable with GraphQL and using it in a Vue app.

What we'll be building

For the purpose of this tutorial, we'll be building a simple photo album app, where users will be able to upload as well as view their photos. All the photos will be uploaded directly to Cloudinary. Below is a quick demo of the final app:

File Upload with Vue, Apollo Client and GraphQL

Getting your Cloudinary keys

Before we dive into code, let’s make sure we have our Cloudinary key in place. If you don’t already have an account with them, you can signup for free. Otherwise, login to your dashboard where you would see your account details along with your keys.

File Upload with Vue, Apollo Client and GraphQL

Building the GraphQL server

Now, let's start building the GraphQL server. First, let's create our project directory:

$ mkdir graphql-vue-photo-upload && cd graphql-vue-photo-upload
$ mkdir server && cd server
$ npm init -y

All the GraphQL API related code will be inside the server directory. Next, let’s install the necessary dependencies for our GraphQL server:

<pre class="ql-syntax" spellcheck="false">$ npm install graphql apollo-server cloudinary dotenv </pre>

Addition to installing Apollo Server and it dependence, we also install the Cloudinary Node.js library and a package to read environment variables.

Once that’s done installing, we can start building the GraphQL server. Create a new src directory and create a new index.js file inside it, then add the following code in it:

// server/src/index.js

const { ApolloServer } = require('apollo-server')
require('dotenv').config()
const typeDefs = require('./schema')
const resolvers = require('./resolvers')

const server = new ApolloServer({
  typeDefs,
  resolvers
})

server.listen().then(({ url }) => console.log(`Server ready at ${url}`))

Next, we need to create the schema and resolvers which our GraphQL server references. We’ll start with creating the schema. Create a schema.js inside src directory and paste the following code into it:

// server/src/schema.js

const { gql } = require('apollo-server')

const typeDefs = gql`type Photo {
    filename: String!
    path: String!
  }

  type Query {
    allPhotos: [Photo]
  }

  type Mutation {
    uploadPhoto(photo: Upload!): Photo!
  }`

module.exports = typeDefs

Here, we define a Photo type that comprises of two fields: the filename of the photo and the path to the actual photo. Then we define a single query allPhotos for fetching all uploaded photos. Lastly, we have a mutation for uploading a photo. The uploadPhoto mutation accepts a single argument, which is the photo that is to be uploaded. The argument is of the scalar type Upload, which is made available to us my Apollo Server, since it has built-in support of file upload. The mutation will return the uploaded photo.

The next, thing to do is create the resolvers. Still inside src directory, create a resolvers.js and add the code below in it:

// server/src/resolvers.js

const cloudinary = require('cloudinary').v2

cloudinary.config({
  cloud_name: process.env.CLOUD_NAME,
  api_key: process.env.API_KEY,
  api_secret: process.env.API_SECRET
})

const photos = []

const resolvers = {
  Query: {
    allPhotos () {
      return photos
    }
  },
  Mutation: {
    async uploadPhoto (parent, { photo }) {
      const { filename, createReadStream } = await photo

      try {
        const result = await new Promise((resolve, reject) => {
          createReadStream().pipe(
            cloudinary.uploader.upload_stream((error, result) => {
              if (error) {
                reject(error)
              }

              resolve(result)
            })
          )
        })

        const newPhoto = { filename, path: result.secure_url }

        photos.push(newPhoto)

        return newPhoto
      } catch (err) {
        console.log(err)
      }
    }
  }
}

module.exports = resolvers

First, we pull in the Cloudinary library and configured it will our credentials, which are getting from the environment variables. Then we create an empty array, which will hold our photos. Next, we define the resolver for the allPhotos query, which simply returns the photos array.

For the uploadPhoto mutation, Apollo Server would return the selected file as Promise, which contains a bunch of details about the file, such as: createReadStream, filename, mimetype and encoding. In this tutorial, we’ll only be making use of the first two, so we extract them from the object. Using createReadStream, we stream the file directly to Cloudinary. Since it is an asynchronous operation we wrap it in a Promise and awaits it. If the Promise was resolved, that is, the file was uploaded successfully to Cloudinary, we create a new object containing the uploaded file name and the Cloudinary path to the file. Then we push the new object to the photos array and finally return the new object.

Lastly, if there was an error uploading the file to Cloudinary, we simply console log the erorr.

Before we wrap up the GraphQL API, let’s quickly add our environment variables. Create a .env file directly in the server directory and add the code below in it:

// server/.env

CLOUD_NAME=YOUR_CLOUD_NAME
API_KEY=YOUR_API_KEY
API_SECRET=YOUR_API_SECRET

Remember to replace the placeholders with your actual account details.

Finally, let’s start the server:

<pre class="ql-syntax" spellcheck="false">$ node src/index.js </pre>

The server should be running on [[http://localhost:4000](http://localhost:4000)](http://localhost:4000](http://localhost:4000))

Building the frontend app

As already mentioned, the frontend app will be built with Vue.js, so let’s create a new Vue.js app using the Vue CLI:

<pre class="ql-syntax" spellcheck="false">$ vue create client </pre>

When prompted, press enter to select the default preset. Start the app and leave it running while we build on it:

$ cd client
$ yarn serve

The app should be running on http://localhost:8080

Once the Vue app is created, let’s install the necessary dependencies:

<pre class="ql-syntax" spellcheck="false">$ npm install vue-apollo graphql-tag graphql apollo-cache-inmemory apollo-client apollo-upload-client </pre>

Out of these dependencies, the one that is new and that I’d like to point out is [apollo-upload-client]([https://github.com/jaydenseric/apollo-upload-client)](https://github.com/jaydenseric/apollo-upload-client)). It’s a package for Apollo Client that allows us to send GraphQL multipart requests. It will be used in place of apollo-link.

Next, let’s configure Vue Apollo and these dependencies. Update main.js as below:

// client/src/main.js

import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { createUploadLink } from 'apollo-upload-client'
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import App from './App.vue'

Vue.config.productionTip = false

Vue.use(VueApollo)

const apolloClient = new ApolloClient({
  link: createUploadLink({ uri: 'http://localhost:4000' }),
  cache: new InMemoryCache()
})

const apolloProvider = new VueApollo({
  defaultClient: apolloClient
})

new Vue({
  apolloProvider,
  render: h => h(App)
}).$mount('#app')

You’ll notice here we are using createUploadLink from apollo-upload-client to create the ApolloClient link, passing to it our GraphQL API endpoint.

To give our app a bit of styling, we’ll be pulling in UIKit. Add the line below to head section of index.html:

<!-- client/public/index.html -->

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.1.5/css/uikit.min.css" />

Fetching all photos

We’ll start with the GraphQL query for fetching all our photo. Create a graphql directory inside the client/src directory and with in, create a AllPhotos.js file and paste the code below into it:

// client/src/graphql/AllPhotos.js

import gql from 'graphql-tag'

export default gql`query allPhotos {
    allPhotos {
      filename
      path
    }
  }`

For the sake of simplicity, we’ll be making use of just the App.vue component. So let’s update it as below:

// client/src/App.vue

<template>
  <section class="uk-section">
    <div class="uk-container uk-container-small">
      <h2>Photo Album</h2>

      <div class="uk-grid [email protected]">
        <div class="uk-margin" v-for="(photo, index) in allPhotos" :key="index">
          <div class="uk-card uk-card-default">
            <div class="uk-card-media-top">
              <img :src="photo.path">
            </div>
            <div class="uk-card-body">{{ photo.filename }}</div>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import ALL_PHOTOS from "./graphql/AllPhotos";

export default {
  name: "app",
  apollo: {
    allPhotos: ALL_PHOTOS
  }
};
</script>

Within the apollo object, we add the ALL_PHOTOS query to fetch all photos and save it in a allPhotos. Once allPhotos is populated with data from our GraphQL API, we display the photos by looping through them.

If we view our app, we should get something similar to below:
File Upload with Vue, Apollo Client and GraphQL

Uploading photo

Of course, we need to have uploaded some photos before we can see them. Let’s implement that now. Still inside the graphql directory, create a UploadPhoto.js and paste the code below into it:

// client/src/graphql/UploadPhoto.js

import gql from 'graphql-tag'

export default gql`mutation uploadPhoto($photo: Upload!) {
    uploadPhoto(photo: $photo) {
      filename
      path
    }
  }`

Next, add the snippet below to the template section of App.vue, just below the Photo Album heading:

// client/src/App.vue

<div class="uk-margin">
  <input type="file" accept="image/*" @change="uploadPhoto">
</div>

Here, we have a file input field that accepts only images. On change of the input field a uploadPhoto method is triggered.

In the script section, add:

// client/src/App.vue

import UPLOAD_PHOTO from "./graphql/UploadPhoto";

methods: {
  async uploadPhoto({ target }) {
    await this.$apollo.mutate({
      mutation: UPLOAD_PHOTO,
      variables: {
        photo: target.files[0]
      },
      update: (store, { data: { uploadPhoto } }) => {
        const data = store.readQuery({ query: ALL_PHOTOS });

        data.allPhotos.push(uploadPhoto);

        store.writeQuery({ query: ALL_PHOTOS, data });
      }
    });
  }
}

We get extract the target from the input event, then call the mutate method, passing to it the UPLOAD_PHOTO mutation as well as the required argument (through the variables object). We get the selected file from the files on the target object. Once the mutation is executed, we update the cache by adding the newly uploaded photo to the allPhotos array.
File Upload with Vue, Apollo Client and GraphQL

File Upload with Vue, Apollo Client and GraphQL

Conclusion

So in this tutorial, we have seen how to handle file uploads in GraphQL using Apollo Server on the server side and Vue and Vue Apollo on the client side. Though we used Cloudinary to store our photos, you can easily wrap it for any other cloud storage service or you can even save directly to your own local filesystem.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Vue JS 2 - The Complete Guide (incl. Vue Router & Vuex)

Nuxt.js - Vue.js on Steroids

The Modern GraphQL Bootcamp (Advanced Node.js)

NodeJS - The Complete Guide (incl. MVC, REST APIs, GraphQL)

Implement file uploads and likes in a React chatroom

Ionic 4, Angular 7 and Cordova Crop and Upload Image

A Guide To Compress And Upload Images In Laravel 5.8

How to upload image using Vuejs?

Top 8 Trends and Tools Front-End JavaScript for 2020

Top 8 Trends and Tools Front-End JavaScript for 2020

The JavaScript world is moving fast. The world of frontEnd development (and web development) is moving blazingly fast. Today, if you’re not on top or Webpack, React Hooks, Jest, Vue and NG elements, you start to feel the gap widening. But, things are changing.

While the numbers of both developers and techs in the frontEnd jungle spike from year to year, the ecosystem aspires towards standardization. The emergence of new technologies and tools are already changing the game.

It’s safe to say the general trend will be UI standardization, component-based modularity and composition which affects everything from styling to testing and even state management, and better modularity overall. This will include technologies built around web-components, ES modules, component-focused tools for styling or state management, and much more. This is an opinionated short and partial observation of the state of frontEnd development looking forward a few years.

1. Future of Framework Wars?

This is image title

<figcaption>Yeah, in NPM downloads React is still queen.

So we’re not really going to dive into “who’s better and why”, and you find more answers to that question below. Instead, we’ll take a step back and note the bigger picture. The overall “market-share” for frontEnd technologies around components is growing. Constantly. The rate of new developers joining in is also fast-growing, and there’s more room for tools to adopt.

So which framework will rule 5 years from now? no one knows. But, it will be safe to say that it will be the one best position to play in the native JS ecosystem where web-components rule the dom. React is positioned at the top of the NPM downloads. **However — **look at these numbers . It seems that in actual web usage, the gap is very small.

Shocking, right?

This is image title

With the future standardization of framework-agnostic web components, one can wonder about the effects it might have on the UI framework-wars. And yes, React is not a framework… we know.

2. Framework Agnostics Web Components

This is image title

So basically, this is the future. Why? because these pure web components are framework agnostic and can work without a framework or with any framework- spelling standardization. Because they are free from JS fatigue and are supported by modern browsers. Because their bundle size and consumption will be optimal, and VDOM rendering is mind-blowing.

These components provide Custom Element, a Javascript API which allows you to define a new kind of html tag, HTML templates to specify layouts, and of course the Shadow DOM which is component-specific by nature.

Prominent tools to know in this space are Lit-html and Lit-element, StencilJS, SvelteJSand of course Bit, for reusable modular components which can be directly shared, consumed and developed anywhere.

When thinking of the future of our UI development, and of how principles of modularity, reusability, encapsulation, and standardization should look like in the era of components, web components are the answer. Learn more below.

3. Component isolation, reuse and composition

This is image title

When speaking of frontEnd development and UI components in the near future, it’s impossible to ignore the amazing promise and abilities of Bit

Bit (open-source) isolates and turns your components (or any reusable JS code) into shared building blocks you can use and share across all your projects and applications. And here comes the magic- you can also use Bit to develop the same components from different projects, while gaining full control over both source code changes and the entire dependency graph.

In simpler words, with Bit you can instantly use a component from one project in another project, develop and change it from both, and sync changes. When working as a team, this workflow becomes enhanced with bit.dev, Bit’s component hub, where you can organize and share your team’s code.

The hub provides everything you need to share and collaborate on components, from a beautiful search and discovery experience to a live component playground, full CI/CD support and much more.

<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FE5lgoz6-nfs%3Ffeature%3Doembed&amp;url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DE5lgoz6-nfs&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FE5lgoz6-nfs%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no">https://medium.com/media/584325f6121c810af8af8824913e9402/href</iframe>

With Bit, you can build apps with full and instant access to all the components ever written by your team and the open-source community, and instantly share new components or suggest updates to existing ones. Wow.

teambit/bit

4. ES Modules and CDN

This is image title

ES Modules is the standard for working with modules in the browser, standardized by ECMAScript. Using ES modules you can easily encapsulate functionalities into modules which can be consumed via CDN etc. With the release of Firefox 60, all major browsers will support ES modules, and the Node mteam is working on adding ES module support to Node.js. Also, ES module integration for WebAssembly is coming in the next few years. Just imagine, JS components isolated with Bit and consumed via CDN via bit.dev.

5. State management at the component level

This is image title

So… what’s new in state-management? eventually, we just through everything in the Redux global store anyways right?

But, that can make it hard to fully leverage the modular and reusable nature of components. From this perspective, projects like MobX offer an interesting, more reactive approach (also check out unstated). React’s new Context API and Hooks means you don’t need a 3rd party library and can manage states at the level of functional components, improving modularity and reusability.

So looking forward, try thinking of state management more in terms of encapsulated components and less in terms of a global application store.

6. Styling components as composition

This is image title

So there has been a lot of chatter around styling components in the past two years. From inline CSS or CSS modules to CSS in JS and styled components and even half-way solution like stylable, the options are plenty.

When thinking of styling in the next few years, I like to think of styling as a composition. Meaning, our component design system should include both logical components and theming components which can be composed together using tools like Bit. This way, you can create a design system that evolves and changes as needed, and doesn’t force a cumbersome library on developers who are unwilling to adopt it. Design tools themselves, like Sketch an Figma, leverage will components for this purpose (combine them with Bit, and you get the ultimate component design system. This is pretty exciting.

7. GraphQL API clients for data-driven apps

This is image title

So working with GraphQL opens up exciting possibilities for clients through components. Using Apollo you can easily build UI components that fetch data via GraphQL. Combined with Bit, you can import and develop these components right from the consuming projects you’re working on.

Through the smart management of APIs we can simplify the workflow around data-driven application development and speed the dev-velocity of our work. So, it’s definitely worth diving into looking a couple of years forward.

8. Component-based design tools

This is image title

As components become our design system, the gap between designer and developers will be bridged. This will become possible from both ends of the equation, meaning from both designer and developer perspectives.

Sketch already created dependency links between design components so you can design and update designs in a modular way. Integrations to code components are already sprouting, and it’s just a matter of time. Tools like Figma are built from the grounds up based on reusable UI elements. Framer Team are building a tool for designers who code, with a degree of control over turning UI elements into reusable React components. Through Bit, you can turn the components you design into reusable building blocks that can be visually discovered, used and even developed anywhere, bridging the gap from the developer’s end. Bit + component design tool is a powerful future. With Bit and web components via CDN, this means full-blown composition.

Is there any other recent JavaScript tooling research you’d like to add? Post your suggestions and opinions on the current tooling trends for JavaScript, and to keep up.

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!