The Startup
Published in

The Startup

Multi Language Service With Deno And Redis

A SNAKE AND A SMILE

This article is the continues of my previous Multi Language Support With Angular article. In the first article, we wrote the front-end application. Multi Language Course Admin form. And now we will write backend with the new programming language Deno. And we will get the Html Element’s text by selected language from the Redis with Deno.

Deno is the version of NodeJs that has been corrected and adapted to today’s technologies. Deno is written by Rust and Typescript. Its first priority is security. You can write typescript and javascript with Deno. In this article, we will write with typescript.

“In any given moment we have two options: to step forward into growth or step back into safety.”
— Abraham Maslow

You Can Install DENO to Mac with HomeBrew

In this application, we will use the Deno 1.0.3 version. And Typescript 3.9.2 version.

These are my Deno application structure. Controller, service, models, router, config and start page index. All the pages are written by typescript. It looks like Mvc application right ? :) I am using VS Code as an IDE for this application.

Config.ts:

Lets start from config.ts: We will set application host and port here. And I used “Deno.env” object. It is used for reading environment variables.

When you run deno application, you have to give permission for the reading environment.” — allow-env”. Deno builds on security.

“deno run — allow-read — allow-net — allow-env index.ts”

const env = Deno.env.toObject();
export const APP_HOST = env.HOST || "127.0.0.1";
export const APP_PORT = env.PORT || 1923;
Photo by Ernest Brillo on Unsplash

Router.ts:

These import files are our controller pages. We describe routing key, path and parameters on this page. For Clean Code, these descriptions are so important.

“Not: On Deno, you can import file with absolute path

I used “OAK” for middleware framework on Deno. It is used for Deno’s Http server, including a router middleware. It is like NodeJs Express. We will discuss other controllers like “getKey, fillRedis and getGrid” later in this article.

import { Router } from "https://deno.land/x/oak/mod.ts";
import getKey from "./controllers/getKey.ts";
import fillRedis from "./controllers/fillRedis.ts";
import getGrid from "./controllers/getGrid.ts";
const router = new Router();router
.get("/api/getkey/:languageId?/:key?", getKey)
.get("/api/fillRedis", fillRedis)
.get("/api/getGridKeys/:languageId?/:key?", getGrid)
export default router;

Please be attention to the import files extension. “.ts.” If you don’t use any extension on “VS Code,” you get an error when you import files with any extension.

I used Deno “denoland” extension for the VS Code. So, in the end, I didn’t get any errors because of the “.ts “extension for import files.

  • api/getkey” => Routing key of the “getKey” controller. “languageId?” and “key?” are nullable parameters. Other routes are like this example.
Photo by Romson Preechawit on Unsplash

Controller/404.ts:

We will use this controller on Index.ts. If we can’t find any page on the router, we will send the client this error response message.

export default (response: any) => {
response.status = 404;
response.body = { msg: "There is No Page" };
};

Controller/errorHandler.ts:

We will use this controller on Index.ts too. If we get any error on the page, we will send the client this response message.

export default async (response: any, nextFn: any) => {
try {
await nextFn();
} catch (err) {
response.status = 500;
response.body = { msg: err.message };
}
};

Controller/fillRedis.ts:

This method is used for filling Redis with matching text of all HTML Elements by selected language. We will call “setRedis()” method asynchronously from the redisService.

Not:@ts-ignore” is used not to show an error at the next line.

import { setRedis } from "../service/redisService.ts";export default async ({
//@ts-ignore
params,
//@ts-ignore
response,
}) => {
let result = await setRedis();
response.body = result;
};

Controller/getKey.ts:

This method is used for finding text value of HTML Elements by selected language. We will call “getKey()” asynchronously from the keyService. The returned model is “KeyModel().” We will talk about it a little later.

Controller/getGrid.ts:

This method is used for finding the header’s value of ag-grid by selected language. We will call “getGridKey()” asynchronously from the keyService. We will check the languageId and key parameters. They are required parameters. If one of them is null, we will send an error message. The returned model is “KeyModel().

Index.ts

This is the starting page of this application. I import {Application} from oak, config.ts, router.ts, _404.ts, errorHandler.ts, and finally, “oakCors” for enabling cors.

  • const app = new Application()”: We create the application with oak Application class.
  • app.use(errorHandler)”: We will catch all errors with this class.
  • app.use(oakCors“ : oakCors is used for not getting an error when someone sends the request directly from the client-side to the deno server side. For security, we defined the “localhost” URL with port(1234,3000,4200) on origin property.
  • app.use(router.routes())”: We use router class for filtering the controller classes with the URL.
  • app.use(router.allowedMethods())”: Returns separate middleware for responding to `OPTIONS` requests with. Router.post. HTTP post method.
  • app.use(_404)”: When there is no page found after filtering the controller classes with the URL, we will show this class as a response.
  • await app.listen(`${APP_HOST}:${APP_PORT}`)”: Index.ts page can be reached from “localhost:1923”, we set the “host” and “port” on “config.ts” page.

Index.ts:

Models/enum.ts: I used enum for the language parameter. New languages could be added. So language enum is the best choice for the clean code in this application.

export enum Languages {
Turkish = 0,
English = 1
}

Models/keyModel.ts: This is the Html Element Text response model on the “getKey()” service.

  • key” is the HTML Element ID.
  • value” “value” is the selected language value of the HTML element.
  • languageId” is the selected language of Id.
  • error: If there is no value on the Redis with this keyword, we will return any message error.
export class KeyModel {key: String;
value: String;
languageID: String;
error: String;
constructor(_key: String, _value: String, _languageID: String, _error: String="") {
this.key = _key;
this.value = _value;
this.languageID = _languageID;
this.error=_error;
}
}

Models/columnDef.ts: This is the Ag-Grid column text response model on the “getGridKey()” service.

export class ColumnDefs {headerName: String;
field: String;
constructor(_headerName: String, _field: String) {
this.headerName = _headerName;
this.field = _field;
}
}
Image Source

Service/redis.ts: This redis object is used to connect to the local Redis server. The default port is 6379. We use Redis for improving performance.

import { connect } from "https://denopkg.com/keroxp/deno-redis/mod.ts";export const redis = await connect({
hostname: "127.0.0.1",
port: 6379
});

Service/redisService.ts: This service works once when Redis wake up. It is used for pushing all the HTML elements key to the Redis server. I didn’t want to use any DB for this application. So I pushed all the static keys to the Redis.

  • const courseName = await redis.set(`${Languages.Turkish}-courseName`, “Kurs Adı”)”: We will set asynchronously “courseName” key’s Turkish value to the Redis.
  • const courseName1 = await redis.set(`${Languages.English}-courseName`, “Course Name”)” : We will set asynchronously “courseName” key’s English value to the Redis.
  • const grid= await redis.hset(`${Languages.Turkish}-grid`, “Name”,”Kurs Adı”,“Price”,”Kurs Fiyat”,”TotalHours”,”Kurs Süresi”,”Id”, “Id”)”: We will set asynchronously ag-grid header key’s Turkish value to the Redis as hset.

Redis HSET command is used to set the field in the hash stored at the key to value. You can save more than one rows under the one key, like table on Redis.

1-grid Hset on Redis

redisService.ts:

All Course Admin form texts are stored on Redis like below picture after calling “setRedis()” service.

Local Redis Server

Service/keyService.ts:

This is the main service file. We will set and get all Html Element’s text from Redis by selected language.

  • export const getKey = async (languageId: number, key: string) => {”: getKey() method works asynchronous and gets two parameters: languageId and key.
  • const resultKey = await redis.get(`${languageId}-${key}`)” : The first parameter is the languageID Parameter “1-”. The second parameter is the key “courseName.” Key looks like “1-courseName.” We will get the “courseName” text’s English value from the Redis asynchronously.
  • var model = new KeyModel(key, value, Languages[languageId])”: KeyModel is our result model. We talked before.
Ag-Grid Header
  • export const getGridKey = async (languageId: number, key: string) => {“: This service is used for getting header texts of Ag-Grid. The first parameter is the languageID Parameter “1-”. The second parameter is the key “grid.” Key looks like “1-grid” on Redis.
  • const resultKey = await redis.hgetall(`${languageId}-${key}`)” : “hgetall()” is used to get all rows data with a single key.
  • if (resultKey) { let modelList = Array<ColumnDefs>()” : We will return an array of “columnDefs” with this “getGridKey()” method.
  • resultKey.forEach((item) => {if (oldkey != “”) {” : We will iterate on “ResultsKey.” We will get the data in groups of 2. The first result is the field; the second is its header name on ag-grid. “oldkey” is a field. And we will reset after every two groups. “oldkey!=” is mean that is the second item of the ColumnDefs model.
  • var model = new ColumnDefs(item, oldkey)” : The first item “oldKey” is a field, and the second item “item” is the header. ColumndDefs is our ag-grid column model.
  • modelList.push(model); oldkey = “””: We will push the column item to our return model “modelList”. And we will reset the “oldkey” for the next group.
  • else {return null;}” : If we don’t find any item with this ag-grid key, we will return the null value.

keyService.ts:

deno run --allow-read --allow-net --allow-env index.ts

If you run deno with above this command, you have to see like that screen as below.

Deno is waiting for your requests from 1923 port

This is the Final Screen of Course Admin Form With Multi Language Support

Get All Keys From Deno Servis on Angular
Image Source

Angular Project Update For Deno

Services/courseService.ts:

We will delete all static text data methods as we wrote at previews article and write “get()” Deno services.We removed “GetColumnDefData()” and added new “GetColumnOfGridText()” method. We got all ag-grid headers text from Redis by selected language.

Don’t forget to add the Observable “rxjs” library because Deno services are response asynchronously. So we have to wait for the results. And of course, we will add the HttpClient library for request Deno Service.

This is the root URL path of Deno Service.

This “GetValueOfText()” method is used for getting the text of buttons, labels, and error messages for this application. We will call “getKey” Controller from the Deno service and get asynchronously text of HTML Element. And finally, we will return the result as an “Observable<LabelDB>” data.

This GetColumnOfGridText() method is used for getting the headers text of ag-grid. We will call “getGridKeys” Controller from Deno and get asynchronously List of ColumnDefs data. And finally, we will return the result as an “Observable<ColumnDefs>” data.

courseService.ts:

Not: “Deno service returns result asynchronously. So on Angular, all services must return Observable result. On the client-side, when the Html page is rendered, we have to wait for the async result. If not, the response result can not be seen on the client page.”

You may not control all of the events that happen to you, but you can decide not to be reduced by them.

— MAYA ANGELOU

label.component.ts (Example):

We directly set value“GetValueOfText()” service response to the responseModel. So responseModel is Observable<LabelDB>.

.
.
responseModel=new Observable<LabelDB>();
constructor(private service: CourseService) {}
ngOnChanges(changes: SimpleChanges) {
if (changes.languageId) {
this.responseModel = this.service.GetValueOfText(this.id, this.languageId);}
}

label.component.html(Example):

“We have to wait for async service result with pipe `|.` For waiting with the pipe, the model must be observable. If not, responseModel can not be set at the start of the page. And the label text will be null.” We have to use `|`for every HTML Element. And we have set an alias to the model with async.”

.
<span class="label success" *ngIf="responseModel | async as model">{{ model.value }}</span></th>

app.component.html/ag-grid : On ag-grid everything is the same as label.component. We have to wait for the result of columnDefList with “|” and set an alias to model with async.

<app-grid [rowData]="courseList" *ngIf="columnDefList | async as model" [columnDefs]="model" (selectedData)="getSelectedData($event)"></app-grid>

app.component.ts(ag-grid):

this.columnDefList = this.service.GetColumnOfGridText("grid",this.languageID);

Other all Angular Html Components are the same.

Conclusion:

Deno is a multitalented language. It would not be so wrong to predict that it will be the new face of the NodeJs in the future, with its features such as Typescript, asynchronous support, and of course, more secure.

And especially, if you work with Angular, you have to used to writing with Typescript, so Deno must be much more preferable than other backend languages by Angular developer.

In the end, you can attach a URL library like oak or oakCors on your page. Whenever you want to use a dependency library, just state it through the code, and Deno will take care of downloading it. There’s no need to create the package.json file anymore.

import { Application } from "https://deno.land/x/oak/mod.ts";
import { oakCors } from "https://deno.land/x/cors/mod.ts";

In this article, I tried to show you the power of Deno in a real scenario. We examined various topics from Routing to the Controller, Services to Security, Redis integration to Async. I hope you enjoyed it.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Bora Kaşmer

Bora Kaşmer

2.1K Followers

I have been coding since 1993. I am computer and civil engineer. Microsoft MVP. Senior Software Architect. Ride motorcycle. Gamer. Have two daughters.