Writing Your First Flutter Application With Deno and Postgres

A brief demonstration on how to build your Flutter App with Deno and Backend on Postgres.

Writing Your First Flutter Application With Deno and Postgres

We are back with Deno - the Server-side Runtime which was launched recently and is promisingly getting a lot of lauds from the Developer community. Obviously, it's time for us to get our hands dirty by doing the practical implementation of building a running application!

Read my previous article here to know everything about Deno and what it promises to give to the Developer community.

So, without any further delay, let's get into the practical implementation of Deno with a Flutter Application.

Why Flutter with Deno?

Flutter has been proven as the most propitious Mobile App Development framework. Hence, it would be great to have Flutter as a companion while experimenting with Deno.   

Additives

  • Flutter - The Frontend Mobile User Interface Development framework by Google.
  • Deno - The server-side runtime.
  • Postgres - The Relational Database
  • Android Studio and VS Code - The IDE for Coding (Your preference!)

Concept of the Application

Let's build a sample application that posts poems written by you. Also, let the home screen be similar to the feed page of any social media application you have used. I'd like to name this application Scribblings.


Scribblings - Steps to make the server-side and Database

Every action becomes a process with data - Me!

To start with, we need some data to support the app. Hence, we shall set up a Postgres server on the local machine by downloading the Postgres Installer from the official website here.

I personally prefer Postico - The UI interface for Postgres DB, which helps me to dump the data and create tables easily. It's completely your choice to stick with pgadmin or to go for Postico which can be downloaded here.

A deep analysis on data modeling helps fix the data structure. So, after analysis, the fields required for our application along with their types are listed as follows :

  • id - integer
  • name - text
  • poem - text
  • description - text
  • createdAt - timestamp with time zone
  • updatedAt - timestamp with time zone

The table with fields is created under a specific database. 

Hence, after creating the table under the database poems, your table will look similar to this :

Now, since the data part is ready, let's look into the Server and Routes setup.

Before choosing, you must know about the following modules that help:

Oak - A middleware framework for Deno's http server, including a router middleware.

deno-postgres - PostgreSQL driver for Deno

Denon = A TS based script sed for running a server similar to nodemon. You can use the denon start command to keep running the server. 

Now we'll go step by step in building the Serverside application!

Config File: 

To Connect with any Database, we need a Client to contact which takes the credentials as a token. Here we create a new file called config.ts and an object with all the database credentials and export it so that it can be sed commonly along the files.

Controller File: 

We need to create controller file to control the routes. This is where the Oak framework and Deno-postgres modules are used. We name this file as user_controller.ts

The first step is to import the required modules. As Deno supports URL imports for modules, I'm directly importing the Oak framework from the Deno Third Party Module Library. Also, I'm importing the dbCredentials object we created from config.ts file:

import {dbCredentials} from '../models/config.ts';
import { Client } from "https://deno.land/x/postgres/mod.ts";

Now I am creating a method getPosts to perform the GET request. Since it takes the context for the arrow function, I am naming the object as response. As an additional advantage of TypeScript, I am specifying the type of response as any.

async ({ response }: { response: any }) => {}
Now, create a client object using the Client() class by passing the dbCredentials as follows:
const client = await new Client(dbCredentials);
You may wonder why I am using the async/await keywords method. It is fortunate that Deno supports asynchronous actions that the likes of async/await promise.

Now it's time for the core logic. For better structural advantage, we prefer to write the logic inside try-catch blocks.

Establish the connection with the database using client.connect() method.
await client.connect()
Write a raw query, await the response and store them in a variable using client.query() method.
const result = await client.query("SELECT * FROM posts")
Getting into some TypeScript magic, to process the data which comes as a raw query result, we create an object, get the field and values from each row, bind them into a JSON kind of format and push into an array of posts.
const posts = new Array()    
result.rows.map(p => {
let obj: any = new Object()
result.rowDescription.columns.map((element, index) => {
obj[element.name] = p[index]
})
posts.push(obj)
})
To match the response, we bind and send the data as follows:
response.body = {
success: true,
data: posts
}
To manipulate the error and close the connection, we use the catch and finally blocks.
catch (err) {
response.status = 500
response.body = {
success: false,
msg: err.toString()
}
} 
finally {
await client.end()
}​

Finally, the user_controller.ts file will be conceived like this :

Routes File: 

We need to design the routes for using the Controller methods we created. Hence, we call the Router method from the Oak Standard Library. Also, we need to import the getPosts method from the controller file.

import { Router } from 'https://deno.land/x/oak/mod.ts'
import { getPosts } from '../controller/user_controller.ts'

Now create an instance for the Router() class and define the GET request using the router.get() method. This takes two parameters - the endpoint and the method to be called. In the end, export the route created.

//@description      Create a Router Variable to handle the routes
const router = new Router()

//@description      Designing a Route
router.get('/api/posts', getPosts)
    
export default router

The user_route.ts file looks like this

Server File: 

We are at the end of the server-side application. Now we can set up a new application by creating an instance to the Application() class called app.

const app = new Application()

We mention the instance to consume all the routes using app.use() method

//@description       Use all the routes
app.use(router.routes())
app.use(router.allowedMethods())

We also seek the help of .env file to mention the PORT specifications for Deno Server.

To Kickstart the Server and run the application we use the app.listen() method passing the port variable.

await app.listen({ port: +port })

 

Obviously, it is time to harvest the result of the efforts put into building this app. In your terminal, go for denon start command after setting up the configuration and flag values to run the server.ts file. I use my local WiFi IP Address(localhost) as the server along with the Route endpoint to look at the result (Since I am going to access the server sing Mobile Application) with the help of Postman!

Scribblings - Steps to make the Flutter UI and Integrations

Good Start is half the way done - Anonymous

Now that we have successfully integrated the API, let's move on to the UI creation and Integration in Flutter.

API Handler File:

First, we need to create an API service to call the operations we have in the server-side operations. So, we create a service to perform the actions named APIHandler.dart. To perform the actions, I need to import the http module inside the pubspec.yaml file.

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:scribblings/services/config.dart';

class APIHandler {
  static Future<dynamic> get(url) async {
    print("hitting it inside the get request");
    print(config["baseUrl"] + url);
    return http
        .get(
      config["baseUrl"] + url,
    )
        .then((http.Response response) {
      final int statusCode = response.statusCode;
      print("Status code is $statusCode");
      if (statusCode < 200 || statusCode > 400 || json == null) {
        throw new Exception("${response.body.toString()}");
      }
      return utf8.decode(response.bodyBytes);
    });
  }

Here, the config has been fetched from my local Wifi IP Address as we are connecting a Mobile Application:

Model File:

The ultimate aim of modeling the data is to process the response coming from the Deno server into a readable object. Hence, we use a JSON to Dart Converter online to convert the JSON object response(Fetched from PostMan) into a model object of posts. This has been written inside a file called feed_card_model.dart inside the folder called models.

class FeedCardModel {
  int id;
  String name;
  String poem;
  String createdAt;
  String description;
  String updatedAt;

  FeedCardModel(
      {this.id,
        this.name,
        this.poem,
        this.createdAt,
        this.description,
        this.updatedAt});

  FeedCardModel.fromJson(Map<String, dynamic> json) {
    id = json['id'];
    name = json['name'];
    poem = json['poem'];
    createdAt = json['createdAt'];
    description = json['description'];
    updatedAt = json['updatedAt'];
  }
  
  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['id'] = this.id;
    data['name'] = this.name;
    data['poem'] = this.poem;
    data['createdAt'] = this.createdAt;
    data['description'] = this.description;
    data['updatedAt'] = this.updatedAt;
    return data;
  }
}

Controller File:

Now let us create a Controller file to send the endpoint to call the respective route. I am creating a file called post_controller.dart inside the folder controllers. Here I'm using the following line to call the respective route designed:

data = await APIHandler.get('api/posts');

Note: As usual, this has to be written with await support inside a try/catch block.

Further action is to convert the response object into models. Hence, we decode the JSON data, loop along with the data(Remember the success message and data that comes inside response.body!), convert each row data into a FeedCardModel, then push them to a List of type FeedCardModel named feedList.

try {
      data = await APIHandler.get('api/posts');
      data = json.decode(data);
      data["data"].forEach((post) {
        Map postJsonData = post;
        FeedCardModel obj = FeedCardModel.fromJson(postJsonData);
        feedList.add(obj);
      });
}

Posts Page: 

We just need a Post Feed page to consume all the values that we fetch from the feedList in the controller and show them as individual posts. Hence, I created a class called DashboardCard as a stateful widget. Here, I take the instance of the Controller, call the allPosts method and access the feedList to consume the values in the screen.

PostController postController = PostController()​

  Container(
            height: 300,
            width: MediaQuery.of(context).size.width,
            color: Colors.white,
            child: Center(
              child: Text(
                widget.feedList.poem,
                style: TextStyle(fontFamily: 'Sacramento', fontSize: 28),
              ),
            ),
          ),
          Container(
            color: Colors.white,
            child: Column(
              children: <Widget>[
                SizedBox(
                  height: 20,
                ),
                Container(
                  child: Center(
                    child: Text(
                      widget.feedList.description,
                      style: TextStyle(
                        color: Colors.blue,
                        fontSize: 18,
                        fontStyle: FontStyle.italic,
                      ),
                    ),
                  )

 Your end result is like :

 It is important to note that I have used additional support in our Flutter Application :

  • State Management Technique- Provider
  • Font Style -  Sacramento - To use custom font

But these are  additional layers you add as per your basic requirements and I leave it upto you!

Resources:

Feel free to raise issues and pitch in your comments in them. That helps us in collaborative growth!


Upshot - The End: 

I strongly believe that this article will give you an insight into how to integrate the output of Deno along with the Flutter Application. Follow me on Twitter  to get interesting updates about Deno!

Make a 👏 sound if you built your first Flutter Application with Deno with this article! 

2020 © All rights reserved. GeekyAnts India Pvt Ltd.