Create a Deno Rest API: A promising start with Alosaur and TypeORM
Tags: deno, typeorm, alosaur
Published: 2020-05-21 14:31:09

When I started writing NodeJS APIs, I rapidly fell in love with NestJS which combines my knowledge of Angular and a strongly structured backend frameworks like Spring.

Today deno is almost there and we’ve already got something quite similar to Nest in the Deno world, it is called Alosaur !

It looks like it was inspired by NestJS as it brings all the good stuff:

- Modular architecture
- Dependency injection
- Angular schematics
- Decorator
- Validator
- Orm
- ... 

## Deno install

If you know nothing about deno, a good starting point could be the [Olivier’s Dev.to](https://dev.to/olivierloverde/deno-1-0-officially-scheduled-on-may-13-review-of-the-features-1kb0) post or you can install it with the following commands :
```
#shell
curl -fsSL https://deno.land/x/install/install.sh | sh 
#powershell
iwr https://deno.land/x/install/install.ps1 -useb | iex
#homebrew
brew install deno
```

Don’t forget to add the environment variables to your .profile (or equivalent) and reload it if needed:

```
export DENO_INSTALL="/home/YOUR_NAME/.deno"
export PATH="$DENO_INSTALL/bin:$PATH"
source ~/.profile # or equivalent
```
You should now be able to run deno from the console: `deno -h`

## Init project

To initialize a new project you really don’t need more than creating a folder that contains a Typescript file eg:`main.ts`

You will be able to run your project by typing `deno run main.ts`

## VSCode integration

When you start copy pasting some code from the documentation, you rapidly face IDE incompatibilities. Import statements are a good example:

![Screen error vscode](https://miro.medium.com/max/2000/1*xsX_2xZK94-36CUgfDe_yQ.png )

While writing this article, there are 2 extensions for Deno. Axetroy’s extension seems to be the more sophisticated. You have to manually activate deno extension in the settings. The author advises you to do it for your workspace only.

__Update__ Use the offical [deno extension](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno)

You can create a __.vscode/settings.json__ containing the following:

```json
{
  "deno.unstable": false,
  "[typescript]": {
      "editor.defaultFormatter": "vscode.typescript-language-features",
   },
   "[typescriptreact]": {
       "editor.defaultFormatter": "axetroy.vscode-deno",
   },
   "deno.enable": true,
}
```

At the same time, once we’re going to use nice features like decorators, we will have to tell VSCode how to handle them. The usual way to do this is by creating a `tsconfig.json` at the root of your project:

```json
{
    "compilerOptions": {
        "plugins": [
            {
                "name": "typescript-deno-plugin"
            }
        ],
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    }
}
```

![Alosaur logo](https://miro.medium.com/max/560/1*qUtAw9ODgSXfcemyioe3Pw.png)

## Start with Alosaur

As I said, Alosaur is very similar to NestJS. What we used to call modules are now called __Areas__. For example, you could create an empty MainArea with the following code:

```ts
import { App, Area } from 'https://deno.land/x/alosaur/src/mod.ts';
// Declare module
@Area({
})
export class MainArea {}
// Create alosaur application
const app = new App({
    areas: [MainArea],
});
app.listen();
```

You will be able to run it by using the appropriate flags:
`deno run --allow-net --config ./tsconfig.json main.area.ts`

- __allow-net__ is used to give permission to use network
- __config__ specifies typescript configuration and especially experimental decorator

## Alosaur controller

As for every app respecting separation of concern, a good starting point could be to implement a controller.

```ts
import { Controller, Get, Area, App } from 'https://deno.land/x/alosaur/src/mod.ts';
@Controller('/users')
export class UserController {
@Get('')
    getAll() {
        return [{id:1, name:"Jack"}];
    }
}
```


As usual, we use the __Controller__ decorator with the parameter “/users” to declare our class in charge of requests on the “/users” endpoint.
We also declare a getAll method decorated with a the Get decorator.

We now need to do add our freshly created controller into our MainArea:

```ts
@Area({
    controllers: [UserController],
})
```
Don’t forget to import UserController correctly *(with the .ts)*

If you run your deno application now, you should be able to get some data on this url: [http://localhost:8000/users](http://localhost:8000/users)

## Service

A proper way to isolate business logic is by creating a service layer. 
We can create a Service with the following:

```ts
// user.service.ts
export class UserService {
    getAll() {
        return [{ id: 1, name: "Jack" }];
    }
}
```

We can now inject UserService directly inside UserController.
Using the constructor declaration is the shortest way to do it:

```ts
export class UserController {
    constructor(private userService:UserService){}
    @Get('')
    getAll() {
        return this.userService.getAll();
    }
}
```

[microsoft/tsyringe](https://github.com/microsoft/tsyringe) is included within Alosaur.
__Tsyringe__ is described by Microsoft as : *"A lightweight dependency injection container for TypeScript/JavaScript for constructor injection."*

__Alosaur's controllers__ are using DI by default and are therefore respecting the IoC (inversion of control) pattern.

## Repository

What we want next is an abstraction for our data access. Repositories are commonly used for that purpose.


````ts
// user.repository.ts
export class UserRepository {
    getAll(){
        return [{ id: 1, name: "Jack" }];
    }
}
```

We will use that repository exactly the same way that we did before with the UserService/UserController.

```ts
export class UserService {
    constructor(private userRepository: UserRepository) {}
    getAll() {
        return this.userRepository.getAll();
    }
}
```

If you run your app now, you're going to face:

> error: Uncaught TypeInfo not known for class UserService

That's because the DI mechanism doesn't know how to deal with that __UserRepository__ inside the constructor. Thankfully, you just have to decorate your __UserService__ with __AutoInjectable__ to solve the issue.

AutoInjectable replaces our constructor by *"a parameterless constructor that has dependencies auto-resolved"*, isn't it beautiful ?

```ts
import { AutoInjectable } from "https://deno.land/x/alosaur/src/mod.ts";
import { UserRepository } from '../repository/user.repository.ts';
@AutoInjectable()
export class UserService {
    constructor(private userRepository: UserRepository) {}
    getAll() {
        return this.userRepository.getAll();
    }
}
```

![Typeorm Logo](https://cdn-images-1.medium.com/max/1200/1*5dfDFiYHqrZSgfp47TYN4Q.jpeg)

## TypeORM Entity
Jack is nice, but Jack is hardcoded into our repository. We want to get data from the database.
 
TypeORM is a powerful micro framework used to deal with databases.
*"TypeORMis highly influenced by other ORMs, such as Hibernate, Doctrine and Entity Framework."*

In this article we're going to use a deno compatible [fork](https://github.com/denolib/typeorm).

The simplest way to start is by creating our first entity: user.entity.ts

```ts
import { Entity, PrimaryGeneratedColumn, Column } from 'https://denolib.com/denolib/typeorm@v0.2.23-rc3/mod.ts';
@Entity()
export class User{
    @PrimaryGeneratedColumn()
    id!: number;
    
    @Column("varchar", { length: 30 })
    name!: string;
}
```

> At the time when I'm writing this article, I have to hardcode typeorm's version. This could change in a near future.

- The __Entity__ decorator indicates that the user class will be persisted *(In relational database context, that will be a table)*
- The __PrimaryGeneratedColumn__ decorator describes our id property as the primary auto generated value (Auto increment id). 

Primary key is mandatory.

## TypeORM configuration

To configure the micro framework we need to pass the correct parameters to the __createConnexion__ function.

```ts
// init-typeorm.ts
import { createConnection } from 'https://denolib.com/denolib/typeorm@v0.2.23-rc3/mod.ts';
export function initTypeORM() {
return createConnection({
        type: "postgres", // mysql is not currently supported 19/05/2020
        host: "172.17.0.2",
        port: 5432,
        username: "postgres",
        password: "pwd",
        database: "postgres", // default database
        entities: [
            "src/entities/*.ts"
        ],
        synchronize: true,
    });
}
```

To be detected by TypeORM, your user entity has to be at this location: __src/entities/user.entity.ts__

To launch the corresponding database on your computer: [docker hub](https://hub.docker.com/_/postgres)

`docker run --name some-postgres -e POSTGRES_PASSWORD=pwd -d postgres`

> On my linux, I can get the container IP (172.17.0.2) by using docker inspect. 
On Mac and windows, you sould be able to connect to the database by using the `--publish=5432:5432` and then connect the db directly on localhost.

We now should call initTypeORM from our main area:

```ts
. . . 
await initTypeORM(); // Init before creating the app
const app = new App({
    areas: [MainArea],
});
app.listen();
```

If everything is configured correctly, we are now able to run our app. The `"syncronize:true"` parameter will __generate missing tables automatically.__

__Not so fast:__ we will have to specify some additional options to deno:

`deno run --unstable --allow-net --allow-read  --config ./tsconfig.json main.area.ts`

__unstable__ because at the current time, TypeORM uses some unstable feature of deno.

__allow-read__ because TypeORM scans the filesystem to dynamically find entities.

## Custom repository
The final step to use all the power of TypeORM is to create a custom repository. To do that, we are going to decorate our repository with __EntityRepository__ and extend __Repository__ :

```ts
import { EntityRepository } from "https://denolib.com/denolib/typeorm@v0.2.23-rc3/src/decorator/EntityRepository.ts";
import { Repository } from "https://denolib.com/denolib/typeorm@v0.2.23-rc3/src/repository/Repository.ts";
import { User } from '../entities/user.entity.ts';

@EntityRepository(User)
export class UserRepository extends Repository<User> {
}
```

Let's change our service to correctly use typeORM's *find* method:

```ts
import { AutoInjectable } from "https://deno.land/x/alosaur/src/mod.ts";
import { getCustomRepository } from "https://denolib.com/denolib/typeorm@v0.2.23-rc3/src/index.ts";
import { UserRepository } from '../repository/user.repository.ts';
@AutoInjectable()
export class UserService {
    private userRepository: UserRepository;
    constructor() {
        this.userRepository = getCustomRepository(UserRepository);
    }
    getAll() {
        return this.userRepository.find();
    }
}
```

> At this point, our app is runnable without the __AutoInjectable__, but there are big chances that on a real app you will need a DI mechanism.

As you can see, we are now using __getCustomRepository__ to get the instance of our repository. That's unfortunately not very consistent with our dependency injection from Microsoft but I didn't find anything better for now.

At this point, a GET request on [http://localhost:8000/users](http://localhost:8000/users) returns an empty array. 

However, If you manually add some data in the user table, you will correctly get it. You could also declare a second endpoint and use TypeORM to save a new user. (check my repository for more examples)

## Conclusion

Deno, success or not ? Future will tell, but I hope this article will help people who would give it a try !

The full source code for the article: [https://github.com/hugoblanc/deno-api](https://github.com/hugoblanc/deno-api)