Writing an API with Deno
Tags: deno, typescript, api, mandarinets
Published: 2020-06-08 03:21:59

In this post, we will be developing a small API system using **Deno** (A runtime for Javascript & Typescript developed by Ryan Dahl, Node.JS creator). 

For our API system we will be using [Mandarine.TS framework](https://mandarineframework.gitbook.io/mandarine-ts/). This framework will help us code & save a lot of time in our design as it has built-in solutions such as Dependency Injection, HTTP Handlers & Routes, and more.

## Goals
- Create an API which manages _books_.
- Provide GET & POST Routes
- Use Mandarine.TS to get you involved with this framework and why it can be important when using Deno for web applications.

# Let's get started.

### Concepts
* Services: Classes Where our business logic is.  
* Controllers: Classes responsible for handling HTTP requests as well as creating the routes for it.
* Mandarine.ts/Mandarine: A Typescript decorator-driven web framework for Deno.

### Now, coding.

#### Step 1
We will be creating a `tsconfig.json` file in the root of our project. Mandarine.TS framework requires a `tsconfig.json` in order to work properly.

_tsconfig.json_
```json
{
    "compilerOptions": {
        "strict": false,
        "noImplicitAny": false,
        "noImplicitThis": false,
        "alwaysStrict": false,
        "strictNullChecks": false,
        "strictFunctionTypes": true,
        "strictPropertyInitialization": false,
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "allowUmdGlobalAccess": false,
    }
}
```

#### Step 2
We will be creating a typescript file and we will call it `books-service.ts`. This file will contain the functioning of our API that we will later be using in our _Controller_. We will locate this file inside a folder called **services**

_books-service.ts_
```typescript
import { Service } from "https://deno.land/x/mandarinets/mod.ts";

let books: Array<IBook> = new Array<IBook>();

interface IBook {
    id: number;
    name: string;
    author: string;
}

@Service()
export class BooksService {

}
```
In code above, we are importing from the repository of Mandarine.TS framework the decorator `@Service()` which will decorate our class `BooksService`, this way, Mandarine can interpret this as a [Mandarine-powered component](https://mandarineframework.gitbook.io/mandarine-ts/mandarine-core/components). We have also declared a variable called `books` which we will be using to add & get books from, and we have created an interface `IBook`, which is simply the structure of the book item.

#### Step 3
Now that we have our service class decorated with `@Service()`, we will be adding some logic to it, we will create 4 methods: `addBook`, `getBookById`, `getBookByName`, `getAllBooks`

```typescript
@Service()
export class BooksService {

    public addBook(id: number, name: string, author: string) {
        if(!books.some(book => book.name == name || book.id == id)) {
            books.push({
                id: id,
                name: name,
                author: author
            });
        }
    }

    public getBookById(id: number): IBook {
        return books.find(book => book.id = id);
    }

    public getBookByName(name: string): IBook {
        return books.find(book => book.name = name);
    }

    public getAllBooks(): Array<IBook> {
        return books;
    }
}
```
- `addBook`: Adds a book only when the **id** or the **name** are available.
- `getBookById`: Look up a book in the array that matches with our provided id.
- `getBookByName`: Look up a book in the array that matches with our provided book name.
- `getAllBooks`: Returns all the books that have been added.

#### Step 4
In this step, we will be creating our controller. For this, we will create a new folder in the root of our project called **controllers**, and in this folder, we will locate our **books-controller.ts** which will contain our controller class.

_books-controller.ts_
```typescript
import { Controller } from "https://deno.land/x/mandarinets/mod.ts";

@Controller('/api')
export class BooksController {
}
```
In the code above, we have declared our controller by using the decorator `@Controller` which will inform Mandarine that this is a class that must be considered a controller [(See official documentation here)](https://mandarineframework.gitbook.io/mandarine-ts/mandarine-core/components/controller). As you can see, we are passing a parameter to our decorator with the value `/api`, this means, all the routes inside our controller will have to start with `/api` when requesting them.

#### Step 5
Now that we have declared our controller, we will **inject** our Service. This is done by importing our Service & putting it in the constructor of our controller.  
Our code will look like this:
```typescript
import { Controller } from "https://deno.land/x/mandarinets/mod.ts";
import { BooksService } from "../services/books-service.ts";

@Controller('/api')
export class BooksController {
    constructor(private readonly booksService: BooksService){}
}
```
By injecting our service, Mandarine will resolve all its dependencies (if it has) and will make it available for us to use in our controller component without needing to manually initialize it. [(See more documentation about this here)](https://mandarineframework.gitbook.io/mandarine-ts/mandarine-core/dependency-injection)

#### Step 6
Now that we have our controller & we have also injected our book service (`BooksService`), we will proceed to create 3 endpoints:
- `/api/books/add-sample`: It will add default books to our books array when requesting it.
- `/api/books/getAll`: It will return all the books that have been added.
- `/api/books/get`: It will get a book from the array by filtering by ID or book name.

Our controller will look like this
```typescript
import { Controller, GET, POST, QueryParam } from "https://deno.land/x/mandarinets/mod.ts";
import { BooksService } from "../services/books-service.ts";

@Controller('/api')
export class BooksController {

    constructor(private readonly booksService: BooksService){}

    @POST('/books/add-sample')
    public addSampleBooksHandler() {
        this.booksService.addBook(1, "The Diary Of a Young Girl", "Anne Frank");
        this.booksService.addBook(2, "Sapiens: A Brief History of Humankind", "Yuval Noah Harari");
        return "Success";
    }

    @GET('/books/getAll')
    public getAllBooks() {
        return this.booksService.getAllBooks();
    }

    @GET('/books/get')
    public getBook(@QueryParam('search') typeOfSearch: string, @QueryParam() data: string) {
        if(typeOfSearch === 'id') {
            // We use <any> so the compiler doesn't complain about using a possible string for a number parameter in getBookById
            return this.booksService.getBookById(<any> data);
        } else {
            return this.booksService.getBookByName(data);
        }
    }

}
```
**Note** that we are importing 3 new items: `GET`, `POST`, and `QueryParam`.

- `GET`: Will create a GET route. It takes one parameter which is the route to create.
- `POST`: Will create a GET route. It takes one parameter which is the route to create.
- `QueryParam`: It is a [HTTP Parameter Decorator](https://mandarineframework.gitbook.io/mandarine-ts/mandarine-mvc/controllers/http-handlers#http-parameter-decorators) which will be responsible for injecting the values from the query parameters of the request into our HTTP handler. Refer to the link for a deeper explanation.

#### Step 7
Now that we have our service & controller for our books, we will proceed to create the entry point file. If you have got to this step, congratulations, this step is extremely easy & straight forward as we are just importing our classes to a common place.
This entry point file we will create is the file we will run with **Deno**

We will locate this file in the root of our project and we will call it **entry-point.ts**.

_entry-point.ts_

```typescript
import { MandarineCore } from "https://deno.land/x/mandarinets/mod.ts";
import { BooksService } from "./services/books-service.ts";
import { BooksController } from "./controllers/books-controller.ts";

const services = [BooksService];
const controllers = [BooksController];

new MandarineCore().MVC().run();
```

In the code above, we are basically locating our components in just one place.
`MandarineCore` is possibly the most important part for your code as it handles the engine of Mandarine Framework. This engine will resolve our dependencies & run our website. [(See more here)](https://mandarineframework.gitbook.io/mandarine-ts/mandarine-core/core-initialization).

#### Step 8: Running our Mandarine-powered API.
To run our web application, we will run the following command in the root of our project.
```bash
deno run --config tsconfig.json --allow-read --allow-net entry-point.ts
```

After running that, we should be seeing our engine started which would mean we are ready to request our API.

#### Step 9: Requesting our API

**/api/books/add-sample**:
```http
POST /api/books/add-sample HTTP/1.1
Host: localhost:8080
```
which will return `Success`.

**/api/books/getAll**
```http
GET /api/books/getAll HTTP/1.1
Host: localhost:8080
```
which will return
```json
[
    {
        "id": 1,
        "name": "The Diary Of a Young Girl",
        "author": "Anne Frank"
    },
    {
        "id": 2,
        "name": "Sapiens: A Brief History of Humankind",
        "author": "Yuval Noah Harari"
    }
]
```

**/api/books/get?search=id&data=2**
```http
GET /api/books/get?search=id&data=2 HTTP/1.1
Host: localhost:8080
```
which will return
```json
{
    "id": "2",
    "name": "The Diary Of a Young Girl",
    "author": "Anne Frank"
}
```

## Summary
**What we did**
- We created an API using Deno.
- We created an API using [Mandarine.TS](https://mandarineframework.gitbook.io/mandarine-ts) as our framework.
- We interacted with [Dependency Injection](https://mandarineframework.gitbook.io/mandarine-ts/mandarine-core/dependency-injection)
- We worked with nested routes (Base route in controller to its children)
- We tested our endpoints.

**Why Mandarine.ts was used as the web framework**
- Mandarine saves us a lot of code & functionalities to build any type of web application. This time we used it for an API and in a few lines of code we did a lot with it (Services, Controllers, Handlers, Dependency Injection). 
- Mandarine is decorator-driven, which means, it uses a lot of decorators to interpret functionalities which makes our code more readable and shorter.

## Note
The intention of this article is to show how to build an API with **Deno**. The examples shown are simple examples that are not meant to be used in production environments.

## Disclaimer
I am the creator of Mandarine.TS Framework.

## The end

Get the full source code [by clicking here](https://github.com/andreespirela/deno-tutorials/tree/master/simple-books-api).

Do you have any question or something to say? If so, please leave a comment. If you like this article, please tweet it!