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 }) => {}
Client()
class by passing the dbCredentials
as follows:
const client = await new Client(dbCredentials);
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()
client.query()
method.const result = await client.query("SELECT * FROM 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)
})
response.body = {
success: true,
data: posts
}
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:
- Git Repository for Scribblings - Flutter Application code
- Git Repository for Scribblings - Deno API code
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!