Using PouchDB and SQLite with Ionic 3: A CRUD Example -- Words (1833)

Ionic 3 PouchDB and SQLite

In this tutorial we will learn how to create a CRUD (short for: Create, Read, Update and Delete) mobile application with Ionic 3 (previously Ionic 2) using PouchDB and SQLite.

We will cover these points:

  • What is PouchDB?
  • How to create a new Ionic 3 project using the Ionic CLI?
  • How to install the SQLite 2 Cordova plugin?
  • How to add the PouchDB library?
  • How to add the SQLite adapter for PouchDB?
  • How to create a database service for working with PouchDB?
  • How to create different pages for showing, creating, editing and deleting the database records?
  • Finally how to serve the app locally and using a real device?

This tutorial assumes you already have a development machine ready and setup with:

  • the NodeJS platform and NPM installed.
  • the Ionic CLI installed.
  • Java and Android SDK tools installed in case you want to build your app for Android.
  • a MAC and Xcode for building iOS apps.

So if you are ready! let's get started.

What's PouchDB?

PouchDB in an open source NoSQL (Not only SQL) browser database based on CouchDB. It's created for the sake of enabling developers to build offline first web applications i.e apps which are capable of working offline when there is no network connection, by storing data locally on browser's databases such as the local storage or IndexedDB and also SQLite in case of mobile apps. And syncing data with a CouchDB server when the user is back online.

For Ionic 3, you can either use local storage, IndexedDB or SQLite. The first two options have storage limits but they are faster. For an unlimited storage on mobile devices, either Android or iOS, you can use SQLite.

PouchDB has many features. Let's see them briefly:

  • It's cross-browser: it works in all major browers such as Mozilla Firefox, Google Chrome, Opera, Apple Safari, Microsoft IE (and maybe Edge?) and Node.js platform.
  • it's lightweight: PouchDB is only 46KB in size, when gzipped. You can include it with a simple <script> tag in the browser, or install via npm in NodeJS.
  • It has a short learning curve: the PouchDB API is easy to learn and use.
  • It has syncing capabilities with a CouchDB server out of the box.

Create a New Ionic 3 Project

Let's start by creating a new Ionic 3 project, using the Ionic CLI. So go ahead and open the terminal on Mac/Linux or the command prompt in Windows the run the following command to generate a new project.

$ ionic start ionic3-pouchdb-sqlite blank

Wait for the project to setup the dependencies then navigate inside the root folder:

cd ionic3-pouchdb-sqlite

Next you need to add a bunch of dependencies for enabling PouchDB and SQLite to work.

Installing SQLite Cordova Plugin

On Ionic 2+, the native SQLite database is the most adequate choice when it comes to storing data locally, because it allows to have unlimited storage which is, unfortunately, not the case of local storage or IndexedDB. Also SQLite is a file based database which makes very portable.

You can add SQLite support to your Ionic application with many Cordova plugins such as:

  • Cordova-sqlite-storage: the original Cordova plugin for SQLite.
  • cordova-plugin-sqlite-2: a fork of the previous plugin with extra features.

Let's install the fork with:

$ ionic plugin add cordova-plugin-sqlite-2

Adding PouchDB

To be able to use PouchDB, you need to install a third party library available from npm:

$ npm install pouchdb --save

Adding PouchDB Cordova Adapter for SQLite

Next you'll need to install another third part adapter to use SQLite with PouchDB

$ npm install pouchdb-adapter-cordova-sqlite --save

That's all what you need to install in order to start using PouchDB with SQLite in your Ionic app. Now let's start coding!

We will build a small application that allows you to do CRUD operations i.e create, read, update and delete employees data from a PouchDB + SQLIte database.

The application has many screens: * employees screen: list employees with delete buttons * create an employee screen * update an employee screen

The Database Service

The first thing we need to create is the database service that connects to PouchDB and provides different methods to work with the employees database.

So go ahead and create a service provider using ionic g provider <name>

Make sure you are inside the project's root folder then run the following:

ionic g provider employee

You should have an EmployeeProvider provider generated inside src/providers/employee folder

then add the following code


import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import * as PouchDB from 'pouchdb';  
import cordovaSqlitePlugin from 'pouchdb-adapter-cordova-sqlite';


@Injectable()
export class EmployeeProvider {
  public pdb; 
  public employees;

  createPouchDB() {
        PouchDB.plugin(cordovaSqlitePlugin);
        this.pdb = new PouchDB('employees.db', 
        { adapter: 'cordova-sqlite' });
  }

}

This code will create a SQLite database file named employees.db and initialize the PouchDB database by setting the adapter to cordova-sqlite which instructs PouchDB to use SQLite for storage instead of browser's storage.

Make sure to import the provider into src/app/app.module.ts and add it to the providers array if it's not added automatically.

Also make sure to import the service provider and inject it in the constructor of any component before you can use it.

Now let's add the CRUD methods:

create(employee) {  
    return this.pdb.post(employee);
}   

This method creates a new employee in the database.

post() is a PouchDB API that allows you to create new objects in the PouchDB database.

update(employee) {  
    return this.pdb.put(employee);
}   

This method updates an existing employee in the database. Please note that you need to pass an employee object with the id of the employee to update.

delete(employee) {  
    return this.pdb.delete(employee);
}   

This method deletes an employee from the database.

read() {  
        function allDocs(){
                   this.pdb.allDocs({ include_docs: true})
            .then(docs => {
                this.employees = docs.rows.map(row => {
                    row.doc.Date = new Date(row.doc.Date);
                    return row.doc;
             });


                return this.employees;
            });
          }

          this.pdb.changes({ live: true, since: 'now', include_docs: true})
                    .on('change', ()=>{
                        allDocs().then((emps)=>{

                        this.employees = emps;
                        });
                    });
        return allDocs();

}

The read method simply gets all employees from the database by invoking .allDocs() method which returns a promise that resolves to an array of all employees in the database. The map() maps docs array to docs.rows array which contains data only without PouchDB specific information which, obviously, we don't need!

The code also converts row.doc.Date (stored as JSON ) to JavaScript Date().

We are also listening for changes to PouchDB database so whenever there is a change (create, delete or update after we have retrieved all employees in the start) the function allDocs() gets called again then resolved to update then employees array.

Please note that this is not the most efficient way to update the employees array when any changes occur. You can change this code to only update or delete the affected items not the whole array.

Building App UI Screens

Now that we have created our service provider which takes care of connecting to PouchDB and SQLite and provides all CRUD methods to interface with PouchDB database. Let's create different pages that allows us to list and do actions on the employees database.

We already have a home page generated for us which lives in src/app/pages/home. Open home.html then update the code to show the list of employees using <ion-list>.

<ion-header>
  <ion-navbar>
    <ion-title>The Employees Database</ion-title>
    <ion-buttons end>
      <button ion-button (click)="addEmployee()">
          <ion-icon name="add"></ion-icon>
      </button>
    </ion-buttons>    
  </ion-navbar>
</ion-header>

<ion-content padding>

    <ion-list>
        <ion-item *ngFor="let emp of employees" (click)="showDetails(employee)">
          <div> </div>
        </ion-item>
      </ion-list>

</ion-content>

Next we need to add the code to get employees in src/pages/home/home.ts so open the file then update it to match with the following code:

import { Component } from '@angular/core';
import { NavController , ModalController } from 'ionic-angular';
import { EmployeePage } from './../employee/employee.ts';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  public employees : [] = [];
  constructor(public navCtrl: NavController,public modalCtrl: ModalController, public employeeProvider : EmployeeProvider) {


  }
    ionViewDidEnter() {

            this.employeeProvider.createPouchDB();

            this.employeeProvider.read()
                .then(employees => {
                    this.employees = employees;
                })
                .catch((err)=>{});

    }

    showDetails(employee) {
        let modal = this.modalCtrl.create(EmployeePage, { employee: employee });
        modal.present();
    }  


}

Next we need to generate a new page for employee details so go ahead and run the following command in your terminal:

$ ionic g page employee

This will generate an employee folder inside app/pages/employee with three files. Open employee.html then update it to match the following code:

<ion-header>

  <ion-navbar>
    <ion-title> Employee Details</ion-title>
    <ion-buttons end *ngIf="canDelete">
      <button ion-button (click)="delete()">
          <ion-icon name="trash"></ion-icon>
      </button>
    </ion-buttons>    
  </ion-navbar>

</ion-header>


<ion-content>
    <ion-list>
        <ion-item>
            <ion-label>First Name</ion-label>
            <ion-input text-right type="text" [(ngModel)]="employee.firstName"></ion-input>
        </ion-item>
        <ion-item>
            <ion-label>Last Name</ion-label>
            <ion-input text-right type="text" [(ngModel)]="employee.lastName"></ion-input>
        </ion-item>        
    </ion-list>
    <button ion-button block (click)="addOrUpdate()">Add/Update Employee</button>
</ion-content>

Next update src/pages/employee/employee.ts to add the required logic code

import { Component } from '@angular/core';
import { NavController, NavParams ,ViewController } from 'ionic-angular';



@Component({
  selector: 'employee',
  templateUrl: 'employee.html'
})
export class EmployeePage {
  employee: any = {};
  canDelete : false;
  canUpdate : false;    
  constructor(public navCtrl: NavController, navParams: NavParams, private employeeProvider: EmployeeProvider) {

  }
ionViewDidEnter(){
    var employee = this.navParams.get('employee');
    if(employee){
            this.employee = employee;
            this.canDelete = true;
            this.canUpdate = true;
    }
}

    addOrUpdate() {


        if (this.canUpdate) {
            this.employeeProvider.update(this.employee)
                .catch(()=>{});
        } else {
            this.employeeProvider.create(this.employee)
                .catch(()=>{});
        }

        this.viewCtrl.dismiss(this.employee);
    }

    delete() {
        this.employeeProvider.delete(this.employee)
            .catch(()=>{});

        this.viewCtrl.dismiss(this.employee);
    }



}

Serving the App

We have finished our simple CRUD example with PouchDB and SQLite. Now you can serve your app locally for testing it.

So go ahead and run the following command:

$ ionic serve

You can also plug your Android or iOS device to run the app on a real device instead of the browser.

$ ionic run ios
$ ionic run android

Conclusion

Throughout this tutorial, we have seen, step by step, how create an Ionic 3 mobile application from scratch using the Ionic CLI then added the essential CRUD methods for creating, reading, updating and deleting items from a SQLIte database using PouchDB.


I'm a web developer and technical writer. Passionate about modern JavaScript technologies and currently trying to develop all kind of apps (Web, Mobile and Desktop) with JavaScript.