DEV Community

Milán Tenk
Milán Tenk

Posted on • Updated on

Deno - Angular Stack Proof of Concept

The goal of this post is to give a step-by-step guide on how to set up a Proof of Concept (PoC) project using Deno and Angular. The PoC covers the following topics:

  • Setting up a Deno server
  • Generating an Angular application
  • Serve the Angular application with Deno
  • Call an endpoint of Deno from the Angular app

The result of this guide can be found on GitHub.
The project can be easily used for playing around with this stack. In the following sections, I show how to set it up.

Setting up the Deno server to serve the Angular app

  • First of all, Deno has to be installed based on the installation guide. Please follow the installation instructions and when it is finished check the installation with running deno run https://deno.land/std/examples/welcome.ts from the command line. The result should be similar to this: Alt Text
  • When it is successfully installed, create the project folder. In my case, it is angular-deno-stack-poc. In this folder a subfolder has to be created for the server, the folder name is server.
  • Let's open a Visual Studio Code in angular-deno-stack-poc\server folder and create a TypeScript file called server.ts.
  • To download the index.html of the Angular app following code snippet is needed as first iteration:
import { Application, send } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

app.use(async (context) => {
  await send(context, context.request.url.pathname, {
    root: `${Deno.cwd()}/../client-app/dist/angular-deno-poc`,
    index: "index.html",
  });
});

app.listen({ port: 8080 });
console.log(`Listening on localhost:${8080}`);

The above code uses oak middleware.
Details about it can be found in the official documentation.

The above code snippet assumes that a built Angular application exists on ./../client-app/dist/angular-deno-poc path. I describe in the next section how to create it.

Generate the Angular app

  • Install Node.js LTS if it is not installed yet on the computer.
  • If the Angular CLI is not installed globally yet install it with running npm install -g @angular/cli in the command line. If we type ng --version in the command line and the version is printed out then the installation was successful.
  • In the terminal let's go back to the angular-deno-stack-poc folder and run ng new angular-deno-poc. The Angular CLI asks some questions, I choose to use routing and use scss. Alt Text
  • Rename the created folder to client-app. This way the Angular app is named angular-deno-poc in the development environment and it is located in the client-app folder. Alt Text
  • Navigate in terminal in the client-app folder and run npm run build -- --prod. This way a production build is created in the dist folder. If the build was successful the terminal shows the following output: Alt Text The app is ready, let's check if it can be served with the Deno server.

Check the app in the browser

  • Navigate in command line in angular-deno-stack-poc\server and run deno run --allow-net --allow-read .\server.ts command. The server will listen on port 8080. Alt Text
  • Open a browser and navigate to http://localhost:8080/. If every step was successful, the boilerplate code of Angular CLI is displayed. Alt Text

Now we have a running Deno server that serves the Angular app. As the next step, an endpoint is created and the Angular app will fetch some data from there.

Add an endpoint to the server and fetch data from Angular

We are going to send a message string and a timestamp from the server to the client.

Create a Data Transfer Object

  • First a Data Transfer Object (Dto) is created, which describes the traveling data between the client and the server-side. As we have TypeScript on both sides of the stack, we can use the same TypeScript file for this. Let's create a new folder in angular-deno-stack-poc root folder, it should be called common. Alt Text
  • In the common folder the message-dto.ts file has to be created with the following content:
export interface MessageDto {
    message: string;
    timeStamp: string;
}

Create the endpoint

  • As the next step the server code has to import the file created above and the endpoint has to be created. It means, that after update the server code has to look like this:
import { Application, send, Router } from "https://deno.land/x/oak/mod.ts";
import { MessageDto } from "./../common/message-dto.ts";

const app = new Application();
const router = new Router();

router
  .get("/api/message", (ctx) => {
    const message: MessageDto = {message: "Hello from API!", timeStamp: new Date().toTimeString()}
    ctx.response.body = message;
  });

app.use(router.routes());
app.use(router.allowedMethods());
app.use(async (context) => {
  await send(context, context.request.url.pathname, {
    root: `${Deno.cwd()}/../client-app/dist/angular-deno-poc`,
    index: "index.html",
  });
});

app.listen({ port: 8080 });
console.log(`Listening on localhost:${8080}`);
  • After the code modification resart the Deno server.

  • The endpoint sends response to get requests on /api/message route. It can be check from the browser with navigating to http://localhost:8080/api/message url.
    Alt Text

Add a service to Angular

To fetch data from the server an Angular service has to be used.

  • In angular-deno-stack-poc\client-app\src\app create the core folder and create a file called example.service.ts. The content of it should be the following:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { MessageDto } from '../../../../common/message-dto'


@Injectable()
export class ExampleService {

    constructor(private http: HttpClient) { }

    getExampleMessage(): Observable<MessageDto> {
        return this.http.get<MessageDto>('/api/message');
    }
}
  • In app.module.ts import the HttpClientModule and add ExampleService to the providers. The content of the file should be following:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { ExampleService } from './core/example.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [ExampleService],
  bootstrap: [AppComponent]
})
export class AppModule { }
  • To display the fetched data change app.component.ts content to the below snippet:
import { Component } from '@angular/core';
import { ExampleService } from './core/example.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  message: string;
  timeStamp: string;

  constructor(private exampleService: ExampleService) { }
  ngOnInit() {
    this.exampleService.getExampleMessage().subscribe((result) => {
      this.message = result.message;
      this.timeStamp = result.timeStamp;
    });
  }
}

And change app.component.html to

<h3>Message from the server API:</h3>
<p>{{message}}</p>
<p>{{timeStamp}}</p>
  • After the changes, the Angular app has to be rebuilt. Run npm run build -- --prod from the angular-deno-stack-poc\client-app in command line.
  • After that if the http://localhost:8080/ url is checked in the browser the fetched data is displayed: Alt Text

Conclusion

It was quite easy to get started with Deno. I find it very convenient that it supports TypeScript out of the box. Having the same programming language on frontend and backend side has several benefits, for example in my PoC, I was able to use the same Dto to represent the transferred object.

It would be great to use the same input validation code on frontend and backend side. Do you have any tips or best practices for that? Please let me know. :)

There is a post that continues this topic with setting up the development tools on this stack.

Top comments (1)

Collapse
 
windillusion profile image
windillusion

有什么不一样吗?你用ng build --prod打包出来了html,然后就用deno的server模式托管html?