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: - 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 isserver
. - Let's open a Visual Studio Code in
angular-deno-stack-poc\server
folder and create a TypeScript file calledserver.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 typeng --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 runng new angular-deno-poc
. The Angular CLI asks some questions, I choose to use routing and use scss. - Rename the created folder to
client-app
. This way the Angular app is namedangular-deno-poc
in the development environment and it is located in theclient-app
folder. - Navigate in terminal in the
client-app
folder and runnpm run build -- --prod
. This way a production build is created in thedist
folder. If the build was successful the terminal shows the following output: 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 rundeno run --allow-net --allow-read .\server.ts
command. The server will listen on port 8080. - Open a browser and navigate to
http://localhost:8080/
. If every step was successful, the boilerplate code of Angular CLI is displayed.
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 calledcommon
. - 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 tohttp://localhost:8080/api/message
url.
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 thecore
folder and create a file calledexample.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 theHttpClientModule
and addExampleService
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 theangular-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:
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)
有什么不一样吗?你用ng build --prod打包出来了html,然后就用deno的server模式托管html?