HI WELCOME TO SIRIS

Node.js CRUD example with SQL Server (MSSQL)

Leave a Comment

 In this tutorial, I will show you step by step to build Node.js CRUD example with SQL Server (MSSQL) using Express for Rest API sample project, Sequelize ORM for interacting with MSSQL database.

Node.js CRUD example with SQL Server overview

We will build Rest Apis that can create, retrieve, update, delete and find Tutorials by title.

First, we start with an Express web server. Next, we add configuration for MSSQL database, create Tutorial model with Sequelize, write the controller. Then we define routes for handling all CRUD operations (including custom finder).

The following table shows overview of the Rest APIs that will be exported:

MethodsUrlsActions
GETapi/tutorialsget all Tutorials
GETapi/tutorials/:idget Tutorial by id
POSTapi/tutorialsadd new Tutorial
PUTapi/tutorials/:idupdate Tutorial by id
DELETEapi/tutorials/:idremove Tutorial by id
DELETEapi/tutorialsremove all Tutorials
GETapi/tutorials/publishedfind all published Tutorials
GETapi/tutorials?title=[kw]find all Tutorials which title contains 'kw'

Finally, we’re gonna test the Rest Apis using Postman.

This is our project structure:

node-js-crud-example-sql-server-mssql-project-structure

Create Node.js MSSQL App

First, we create a folder:

$ mkdir node-js-crud-sql-server
$ cd node-js-crud-sql-server

Next, we initialize the Node.js App with a package.json file:

npm init
name: (node-js-crud-sql-server) 
version: (1.0.0) 
description: Node.js CRUD example with SQL Server (MSSQL)
entry point: (index.js) server.js
test command: 
git repository: 
keywords: node js, crud, sql server, mssql, express, sequelize, rest api
author: bezkoder
license: (ISC)
Is this ok? (yes) yes

We need to install necessary modules: expresssequelizetedious.
Run the command:

npm install express sequelize tedious cors --save

The package.json file should look like this:

{
  "name": "node-js-crud-sql-server",
  "version": "1.0.0",
  "description": "Node.js CRUD example with SQL Server",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "node js",
    "sql server",
    "mssql",
    "crud",
    "express",
    "sequelize",
    "rest api"
  ],
  "author": "bezkoder",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "sequelize": "^6.6.5",
    "tedious": "^11.4.0"
  }
}

Connect to SQL Server using Node.js and mssql package

Sequelize Official website recommends tedious as connector library for MSSQL (which we install above).

If you want to use mssql instead of tedious, you can run the command:
npm install mssql

Setup Express web server

In the root folder, let’s create a new server.js file:

const express = require("express");
const cors = require("cors");
const app = express();
var corsOptions = {
  origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
// parse requests of content-type - application/json
app.use(express.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));
// simple route
app.get("/", (req, res) => {
  res.json({ message: "Welcome to bezkoder application." });
});
// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

What we do are:
– import express, and cors modules:

  • Express is for building the Rest apis
  • cors provides Express middleware to enable CORS with various options.

– create an Express app, then add body-parser (json and urlencoded) and cors middlewares using app.use() method. Notice that we set origin: http://localhost:8081.
– define a GET route which is simple for test.
– listen on port 8080 for incoming requests.

Now let’s run the app with command: node server.js.
Open your browser with url http://localhost:8080/, you will see:

node-js-crud-example-sql-server-mssql-setup-server

Yeah, the first step is done. We’re gonna work with Sequelize in the next section.

Configure MSSQL database & Sequelize

In the app folder, we create a separate config folder for configuration with db.config.js file like this:

module.exports = {
  HOST: "localhost",
  PORT: "1434",
  USER: "zkoder",
  PASSWORD: "123456",
  DB: "bezkoder_db",
  dialect: "mssql",
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
};

First five parameters are for SQL Server / MSSQL connection.
pool is optional, it will be used for Sequelize connection pool configuration:

  • max: maximum number of connection in pool
  • min: minimum number of connection in pool
  • idle: maximum time, in milliseconds, that a connection can be idle before being released
  • acquire: maximum time, in milliseconds, that pool will try to get connection before throwing error

For more details, please visit API Reference for the Sequelize constructor.

Initialize Sequelize

We’re gonna initialize Sequelize for working with SQL Server (MSSQL) in app/models folder that will contain model in the next step.

Now create app/models/index.js with the following code:

const dbConfig = require("../config/db.config.js");
const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
  host: dbConfig.HOST,
  port: dbConfig.PORT,
  dialect: dbConfig.dialect,
  pool: {
    max: dbConfig.pool.max,
    min: dbConfig.pool.min,
    acquire: dbConfig.pool.acquire,
    idle: dbConfig.pool.idle,
  },
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.tutorials = require("./tutorial.model.js")(sequelize, Sequelize);
module.exports = db;

Don’t forget to call sync() method in server.js:

...
const app = express();
app.use(...);
const db = require("./app/models");
db.sequelize.sync();
...

In development, you may need to drop existing tables and re-sync database. Just use force: true as following code:


db.sequelize.sync({ force: true }).then(() => {
  console.log("Drop and re-sync db.");
});

Define the Sequelize Model

In models folder, create tutorial.model.js file like this:

module.exports = (sequelize, Sequelize) => {
  const Tutorial = sequelize.define("tutorial", {
    title: {
      type: Sequelize.STRING
    },
    description: {
      type: Sequelize.STRING
    },
    published: {
      type: Sequelize.BOOLEAN
    }
  });
  return Tutorial;
};

This Sequelize Model represents tutorials table in MSSQL database. These columns will be generated automatically: idtitledescriptionpublishedcreatedAtupdatedAt.

After initializing Sequelize, we don’t need to write CRUD functions, Sequelize supports all of them:

  • create a new Tutorial: create(object)
  • find a Tutorial by id: findByPk(id)
  • get all Tutorials: findAll()
  • update a Tutorial by id: update(data, where: { id: id })
  • remove a Tutorial: destroy(where: { id: id })
  • remove all Tutorials: destroy(where: {})
  • find all Tutorials by title: findAll({ where: { title: ... } })

These functions will be used in our Controller.

We can improve the example by adding Comments for each Tutorial. It is the One-to-Many Relationship and I write a tutorial for this at:
Sequelize Associations: One-to-Many example

Or you can add Tags for each Tutorial and add Tutorials to Tag (Many-to-Many Relationship):
Sequelize Many-to-Many Association example

In the Sequelize Associations tutorials above, we use MySQL. But you can easily modify db.config.js to work with SQL Server.

Create the Controller

Inside app/controllers folder, let’s create tutorial.controller.js with these CRUD functions:

  • create
  • findAll
  • findOne
  • update
  • delete
  • deleteAll
  • findAllPublished
const db = require("../models");
const Tutorial = db.tutorials;
const Op = db.Sequelize.Op;
// Create and Save a new Tutorial
exports.create = (req, res) => {
  
};
// Retrieve all Tutorials from the database.
exports.findAll = (req, res) => {
  
};
// Find a single Tutorial with an id
exports.findOne = (req, res) => {
  
};
// Update a Tutorial by the id in the request
exports.update = (req, res) => {
  
};
// Delete a Tutorial with the specified id in the request
exports.delete = (req, res) => {
  
};
// Delete all Tutorials from the database.
exports.deleteAll = (req, res) => {
  
};
// Find all published Tutorials
exports.findAllPublished = (req, res) => {
  
};

Let’s implement these functions.

Create a new object

Create and Save a new Tutorial:

exports.create = (req, res) => {
  // Validate request
  if (!req.body.title) {
    res.status(400).send({
      message: "Content can not be empty!"
    });
    return;
  }
  // Create a Tutorial
  const tutorial = {
    title: req.body.title,
    description: req.body.description,
    published: req.body.published ? req.body.published : false
  };
  // Save Tutorial in the database
  Tutorial.create(tutorial)
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while creating the Tutorial."
      });
    });
};

Retrieve objects (with condition)

Retrieve all Tutorials/ find by title from the database:

exports.findAll = (req, res) => {
  const title = req.query.title;
  var condition = title ? { title: { [Op.like]: `%${title}%` } } : null;
  Tutorial.findAll({ where: condition })
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while retrieving tutorials."
      });
    });
};

We use req.query.title to get query string from the Request and consider it as condition for findAll() method.

Retrieve a single object

Find a single Tutorial with an id:

exports.findOne = (req, res) => {
  const id = req.params.id;
  Tutorial.findByPk(id)
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message: "Error retrieving Tutorial with id=" + id
      });
    });
};

Update an object

Update a Tutorial identified by the id in the request:

exports.update = (req, res) => {
  const id = req.params.id;
  Tutorial.update(req.body, {
    where: { id: id }
  })
    .then(num => {
      if (num == 1) {
        res.send({
          message: "Tutorial was updated successfully."
        });
      } else {
        res.send({
          message: `Cannot update Tutorial with id=${id}. Maybe Tutorial was not found or req.body is empty!`
        });
      }
    })
    .catch(err => {
      res.status(500).send({
        message: "Error updating Tutorial with id=" + id
      });
    });
};

Delete an object

Delete a Tutorial with the specified id:

exports.delete = (req, res) => {
  const id = req.params.id;
  Tutorial.destroy({
    where: { id: id }
  })
    .then(num => {
      if (num == 1) {
        res.send({
          message: "Tutorial was deleted successfully!"
        });
      } else {
        res.send({
          message: `Cannot delete Tutorial with id=${id}. Maybe Tutorial was not found!`
        });
      }
    })
    .catch(err => {
      res.status(500).send({
        message: "Could not delete Tutorial with id=" + id
      });
    });
};

Delete all objects

Delete all Tutorials from the database:

exports.deleteAll = (req, res) => {
  Tutorial.destroy({
    where: {},
    truncate: false
  })
    .then(nums => {
      res.send({ message: `${nums} Tutorials were deleted successfully!` });
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while removing all tutorials."
      });
    });
};

Find all objects by condition

Find all Tutorials with published = true:

exports.findAllPublished = (req, res) => {
  Tutorial.findAll({ where: { published: true } })
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while retrieving tutorials."
      });
    });
};

This controller can be modified a little to return pagination response:

{
    "totalItems": 8,
    "tutorials": [...],
    "totalPages": 3,
    "currentPage": 1
}

You can find more details at:
Server side Pagination in Node.js with Sequelize and MySQL

Define Routes

When a client sends request for an endpoint using HTTP request (GET, POST, PUT, DELETE), we need to determine how the server will reponse by setting up the routes.

These are our routes:

  • /api/tutorials: GET, POST, DELETE
  • /api/tutorials/:id: GET, PUT, DELETE
  • /api/tutorials/published: GET

Create a turorial.routes.js inside app/routes folder with content like this:

module.exports = app => {
  const tutorials = require("../controllers/tutorial.controller.js");
  var router = require("express").Router();
  // Create a new Tutorial
  router.post("/", tutorials.create);
  // Retrieve all Tutorials
  router.get("/", tutorials.findAll);
  // Retrieve all published Tutorials
  router.get("/published", tutorials.findAllPublished);
  // Retrieve a single Tutorial with id
  router.get("/:id", tutorials.findOne);
  // Update a Tutorial with id
  router.put("/:id", tutorials.update);
  // Delete a Tutorial with id
  router.delete("/:id", tutorials.delete);
  // Delete all Tutorials
  router.delete("/", tutorials.deleteAll);
  app.use('/api/tutorials', router);
};

You can see that we use a controller from /controllers/tutorial.controller.js.

We also need to include routes in server.js (right before app.listen()):

...
require("./app/routes/turorial.routes")(app);
// set port, listen for requests
const PORT = ...;
app.listen(...);

Test the APIs

Run our Node.js application with command: node server.js.

The console shows:

Server is running on port 8080.
Executing (default): IF OBJECT_ID('[tutorials]', 'U') IS NULL CREATE TABLE [tutorials] ([id] INTEGER NOT NULL IDENTITY(1,1) , [title] NVARCHAR(255) NULL, [description] NVARCHAR(255) NULL, [published] BIT NULL, [createdAt] DATETIMEOFFSET NOT NULL, [updatedAt] DATETIMEOFFSET NOT NULL, PRIMARY KEY ([id]));
Executing (default): EXEC sys.sp_helpindex @objname = N'[tutorials]';

Using Postman, we’re gonna test all the Apis above.

  1. Create a new Tutorial using POST /tutorials Api

  2. node-js-crud-example-sql-server-mssql-create-tutorial

    After creating some new Tutorials, you can check MSSQL table:

    node-js-crud-example-sql-server-mssql-create-tutorial-database

  3. Retrieve all Tutorials using GET /tutorials Api

  4. node-js-crud-example-sql-server-mssql-retrieve-tutorial

  5. Retrieve a single Tutorial by id using GET /tutorials/:id Api

  6. node-js-crud-example-sql-server-mssql-retrieve-one-tutorial

  7. Update a Tutorial using PUT /tutorials/:id Api

  8. node-js-crud-example-sql-server-mssql-update-tutorial

    Check tutorials table after some rows were updated:

    node-js-crud-example-sql-server-mssql-update-tutorial-database

  9. Find all Tutorials which title contains ‘node’: GET /tutorials?title=node

  10. node-js-crud-example-sql-server-mssql-search-tutorial

  11. Find all published Tutorials using GET /tutorials/published Api

  12. node-js-crud-example-sql-server-mssql-find-tutorial

  13. Delete a Tutorial using DELETE /tutorials/:id Api

  14. node-js-crud-example-sql-server-mssql-delete-one-tutorial

    Tutorial with id=4 was removed from tutorials table:

    node-js-crud-example-sql-server-mssql-delete-one-tutorial-database

  15. Delete all Tutorials using DELETE /tutorials Api

  16. node-js-crud-example-sql-server-mssql-delete-tutorial

    Now there are no rows in tutorials table:

    node-js-crud-example-sql-server-mssql-delete-tutorial-database

You can use the Simple HTTP Client using Axios to check it.

axios-request-example-get-post-put-delete

handling CORS with The Ionic 5 Proxy

Leave a Comment

Ionic 5 allows you to build hybrid mobile apps using web technologies you usually use to build apps. Since building hybrid mobile apps with Ionic 5 is actually building web apps and then bundle them inside a native container with Apache Cordova, the development workflow starts with a local server which serves a web app that can be tested on the browser then can built using Cordova to run on an actual device where it has access to actual native features.

You can also skip serving the app locally with the ionic serve command and use the new Ionic 5 live reload that allows you to use an actual device to test the app and continue developing the app, when your source files changes Ionic pushes the changes instantly to device.

On both situations i.e developing locally with ionic serve or testing using an actual device with live reload, Ionic starts a local development server to serve your app, which can be accessed just like any normal web application from http://localhost:8100/ by default but you can change the port to any available port if you need to.

Handing the Same Origin Policy with the Ionic 5 Proxy

So how to handle same origin policy and CORS issues related to local development? since Ionic uses a local server on both situations we mentionned above so any requests (GET,POST etc.) sent to external servers are marked as sent from the origin localhost:8100.

The same origin policy implemented on browsers states that only clients from the same domain origin can connect with the server with CORS enabled.

You usually get an error similar to:

XMLHttpRequest cannot load [YOUR-SERVER-ENDPOINT]. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access.

What is CORS?

CORS is an abbreviation for Cross origin resource sharing which allows you to control access to your server resources from clients with different origins or simply domains.

For security reasons browsers disallow cross origin requests in JavaScript scripts using XMLHttpRequest or any other mechanism.

CORS provides a few HTTP headers which allow servers to dictate which origins or domains are allowed to access resources from a web browser.

To handle same origin policy and cors issues you can either:

  • Test on a real device without live reload enabled but that's time and efforts consuming.
  • Use Ionic 5 proxy.
  • Changing the CORS headers on the server to allow all or some selected domains to connect to the server.

In this tutorial, we are going to see how to use the second option which is using the Ionic 3 proxy to handle CORS issues when developing hybrid mobile apps locally with Ionic 5 framework.

The Ionic 5 proxy can be especially useful when we don't have the control of the target API server so we can not change the CORS headers to allow the server to accept requests from the local development server origin.

So how to use the proxy in Ionic 3?

Enabling the Ionic 5 Proxy

Using a proxy to bypass the CORS issues with Ionic 5 is actually very easy and straightforward, all you need to do is as follows.

Use a text editor to open ionic.config.json which exists inside the root of your project.

Next, add a proxies array just like in the following example:

    {
    "name": "ionic5-example",
    "app_id": "id",
    "v2": true,
    "typescript": true,
    "proxies": [{
        "path": "/goto",
        "proxyUrl": "http://target-server.com"
    }]
    }

You only need to add the proxies array, leave the other values as they are in your original ionic.config.json.

The proxyUrl should contain the server you want to connect to , which presents the CORS issues.

the path is what we are going to use inside our Ionic project to send requests to the target server, the Ionic 5 proxy will take care of proxying the requests to the actual target API server.

Use the Ionic 5 Proxy

Now how to use these values inside project code?

To use the Ionic 5 proxy, you should replace the base of any URL to the target server with the value of the path. And let Ionic 5 do the hard work of routing the API requests to the target server.

Conclusion

The CORS issues can be annoying if you are developing locally with ionic serve or using a real mobile device with live reload enabled which are the only viable options in development phase. If you don't have control of the server to update the CORS headers, The Ionic 5 proxy can save you a lot of headache by just adding a few lines in the ionic.config.json file.

CORS Issue With Ionic Apps [Solved]

Leave a Comment

 Today we will discuss the solution of the ionic CORS issue, not only with ionic apps but with any other angular projects also.

Most of the time this error comes with the browser only. But if you face ionic cors error on device also, you can follow the below methods.

Do you know what is CORS and why ionic CORS error bother us?

Let’s walk through it

What is the Ionic CORS issue?

CORS stands for cross-origin resources sharing in which origin means a host like example-a.com.

Cross-origin means two different origins like example-a.com and example-b.com and resources sharing means to share data or other content between these origins.

So, now for security purpose browser does not allow the external origin to share the resources that’s why above error you have seen.

For more details about CORS read this https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Now, how to solve this problem.

On Localhost

Like you have run the following command

Ionic serve

And then localhost is started up with a port number something like this:

http://Localhost:8100

and this time your origin is localhost:8100

To get access of data of localhost:8100 by other than this host, need a CORS preflight request.

What is the preflight request?

When a web application trying to make a cross-origin request, it sends preflight request first.

The preflight request needed to know that if the external origin supports CORS or not.

For example, a POST request to an example_b.com with Content-Type of application/json. The preflight request would be:

OPTIONS / HTTP/1.1
Host: example-b.com
Origin: http://localhost:8100
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

If the server is CORS enabled, the server will then respond to this preflight with which origins, methods, and headers are allowed by using the Access-Control-Allow-origin, methods and headers:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8100
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type

If the server is CORS disabled, the request will be blocked by the browser and an ionic cors error will be shown in the console. Otherwise, the request will be made after the preflight.

5 Useful VS-Code extensions for angular or ionic apps read more

Ionic cors issue solution

CORS Disabling by Browser Extension

Chrome and other browser allow us to disable CORS by using their extensions. But CORS used for security reason so, it is not advisable to use browser extensions to disable the CORS.

To know more about CORS read this https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Server configuration for Access-Control-Allow-Origin ionic 4 and ionic 5

Exactly how you enable CORS depends on your server. In general, you need to add a header to the server response that looks like this:

Access-Control-Allow-Origin: *
Or
Access-Control-Allow-Origin: ‘http://example-a.com’

Ionic 4, 5 proxy CORS configuration

We can modify proxies’ array to bypass the  ionic 4, ionic 5 proxy CORS issue. To do this open ionic.config.json locate in the root directory of your ionic project with VS-code or your favourite code editor.

Edit like the following:

{
"name": "myapp-example",
"app_id": "app_id",
"v2": true,
"typescript": true,
"proxies": [{
"path": "/api",
"proxyUrl": "http://example_b.com"
}]
}

Other arrays should remain the same in the ionic.config.json.

The proxyUrl should contain the server you want to connect to, which presents the CORS issues.

the path is what we are going to use inside our Ionic project to send requests to the target server.

Now, change the base of all APIs to the path that is mentioned in ionic.config.json.

Additional server

The additional server is a simple approach to handle the ionic CORS issue. With this method, you need an alternate server with CORS to enable.

Then you can request to this server and this server will send the request to the targeted server and then send response back.

The response should be with required CORS headers.

Downgrade to UIWebView

As we all know ionic use a webView to show the application and UIWebView is an old webView which is now upgraded to WKWebView.

The point is that UIWebView does not force to use CORS but WKWebView does.

Now, you can downgrade to UIWebView to avoid ionic CORS issue but it will sacrifice with the performance which is much better in WKWebView.

Native plugin

The native HTTP plugin helps us to fix the ionic CORS issue but it doesn’t work with the browser. So, we need to test our app with the simulator or real device.

This plugin uses the native code to send the request outside the browser. Native plugin is best to deal with ionic cors error on device.

So, you can use set conditions to check the platform. If the platform is cordova then use native plugin otherwise httpclient will be fine for browsers.

if ( this.platform.is('cordova') ) {
} else {
}

External plugin

Next tip to solve ionic CORS error is to use external plugin ionic-native-http-connection-backend. This library works with backend to fix the CORS issue.

Please note this library will not work with third-party APIs.

How it works:

  • The library provides a HttpBackend interface for Angular’s HttpClient
  • This HttpBackend interface tries to use @ionic-native/http whenever it is possible (= on the device with an installed plugin)
  • If HttpBackend finds it impossible to use @ionic-native/http, it falls back to standard Angular code (HttpXhrBackend, which uses XmlHttpRequest)

This strategy allows developers to use Angular’s HttpClient transparently in both environments: Browser and Device

For more explanation read this https://stackoverflow.com/questions/52801705/ionic-4-cors-issue-on-mobile-device.