Making Rest API with Deno and TypeScript

Making Rest API with Deno and TypeScript

To be honest, I prefer the NodeJS runtime over JavaScript itself. While JS is great, I appreciate the added benefits of type validation and similar features. During my student days, I heavily used C# to build everything from desktop apps to web services, which is probably why I love TypeScript.

Today, I'll show you how to create a Rest API using Deno and TypeScript. It's a powerful combination that I'm excited to share with you.

What is Deno?

Deno is a secure and modern JavaScript/TypeScript runtime created by Ryan Dahl, the same person who created Node.js. It allows you to run JavaScript and TypeScript code outside a web browser, with built-in support for modules, TypeScript, and other valuable features. Deno is becoming increasingly popular among developers due to its security, simplicity, and ease of use.

By the way, if you are struggling with which language to learn first, check out my article about that: Choosing Your First Programming Language: A Beginner’s Guide.

Why Deno?

As I said before, I love TypeScript. Deno allows to run TypeScript directly without compilation in JavaScript.

One more reason is more personal. I like all ideas around Deno project. It would be great to try it with you.

Our Goal

We aim to create a REST API for generating "lorem ipsum" text via user requests. As part of the development process, we'll be implementing a basic security feature: API key validation.

Installing Deno

Deno installation is a simple process. There is an install script that could help do that on different platforms. I like Deno distribution idea like "one single executable". That makes more simple to deploy and use Deno.

Creating project

I suppose you have installed Deno and are ready to continue. Let's create a new directory for our project and then run deno init.

mkdir lorem-ipsum-api-deno-typescript
cd lorem-ipsum-api-deno-typescript
deno init

Let's try to run a boilerplate.

deno run main.ts

Excellent, it's working. Now we are ready to move forward.

Create HTTP end-point

Let's create HTTP end-point. We will use Oak framework for that. Oak is similar to Express framework, which you maybe know because it is very popular in NodeJS community.

The basic code could look like the following.

// import Application class from Oak module
import { Application as OakApp } from "https://deno.land/x/oak/mod.ts";

// create main function of our program
export function runHttp(): void {

    // create new instance of Oak Application
    const app = new OakApp();

    // add handler to our application
    app.use((ctx) => {
        ctx.response.body = "Hello World!";
    });

    // start our application and use promise to show server started or not
    app.listen({ port: 8000 });

    // show message in console
    console.log("Server started on port 8000");
}

// run main function if this file is executed directly
// (not imported as a module)
if (import.meta.main) {
    runHttp();
}

Let's run it and test it. We should add --allow-net parameter to deno run command, because default Deno settings don't allow script access to the networking.

deno run --allow-net main.ts

Nice! Everything is working.

Adding router for lorem ipsum generation

Having all code inside one file and a single function isn't a good idea. Let's make a folder for routes and create there our first router.

// import Router class
import {Router} from "https://deno.land/x/oak/mod.ts";

// create new class that extends Router class
export class LoremIpsumRouter extends Router {

    // create constructor and init routes
    constructor() {
        super();
        this.get("/lorem-ipsum/get-text", this.getText);
    }

    // create handler for GET /lorem-ipsum/get-text
    getText(ctx): void {
        ctx.response.body = "Lorem ipsum dolor sit amet...";
    }
}

Let's import it inside our app.

// import our router class
import { LoremIpsumRouter } from "./routes/lorem-ipsum-router.ts";

And add using a new router by our app.

// create lorem ipsum router
const loremIpsumRouter = new LoremIpsumRouter();

// add handler to our application
app.use(loremIpsumRouter.routes());
app.use(loremIpsumRouter.allowedMethods());

Time to run and test.

Great! Everything is working fine.

Creating handling functions

Let's create a controllers directory and put code for handling requests there. I have created a separate class with static functions. We could use even just functions for that small project without creating classes. However, I put all handling functions inside the same class for a nice structure.

export class LoremIpsumController {

    public static getParagraphs(ctx): void {
        ctx.response.body = "Lorem ipsum dolor sit amet PARAGRAPHS...";
    }

    public static getSentences(ctx): void {
        ctx.response.body = "Lorem ipsum dolor sit amet SENTENCES...";
    }

    public static getWords(ctx): void {
        ctx.response.body = "Lorem ipsum dolor sit amet WORDS...";
    }
}

Now we can change a bit router to make it works with the newly created controller. I have added an optional parameter count to each one. That will allow our API users to specify the desired length of the requested text, sentences, or words. And I connected our controller to the router.

// import Router class
import {Router} from "https://deno.land/x/oak/mod.ts";
import {LoremIpsumController} from "../controllers/lorem-ipsum-controller.ts";

// create new class that extends Router class
export class LoremIpsumRouter extends Router {

    // create constructor and init routes
    constructor() {
        super();
        this.get("/lorem-ipsum/paragraphs/:count?", LoremIpsumController.getParagraphs);
        this.get("/lorem-ipsum/sentences/:count?", LoremIpsumController.getSentences);
        this.get("/lorem-ipsum/words/:count?", LoremIpsumController.getWords);
    }
}

Connect library for generating "lorem ipsum"

Thankfully for npm packages support in Deno we can use an existing library to generate the text. Let's use this package.

In Deno you don't need to install the package. For using lorem ipsum library, let's add those lines inside our controller file.

import { LoremIpsum } from 'npm:lorem-ipsum@2.0.8';

As you can see, it's easy to use npm package in Deno program. The syntax is easy npm:package-name@version.

Controller code for the responses

Time to add actual code for generating the response. Here is our complete controller code.

import { LoremIpsum } from 'npm:lorem-ipsum@2.0.8';

export class LoremIpsumController {

    public static getParagraphs(ctx): void {
        const count = ctx.params.count ? parseInt(ctx.params.count) : 3;
        const lorem = new LoremIpsum();
        ctx.response.body = lorem.generateParagraphs(count);
    }

    public static getSentences(ctx): void {
        const count = ctx.params.count ? parseInt(ctx.params.count)  : 3;
        const lorem = new LoremIpsum();
        ctx.response.body = lorem.generateSentences(count);
    }

    public static getWords(ctx): void {
        const count = ctx.params.count ? parseInt(ctx.params.count)  : 3;
        const lorem = new LoremIpsum();
        ctx.response.body = lorem.generateWords(count);
    }
}

Pretty simple, yeah? Don't forget to convert the count parameter to a number because, by default, it's a string. Also, I added the default count value as 3.

Is it working? Of course, I have tested the system.

deno rest api lorem ipsum test

Auth for Deno Rest API

Oak framework allows us to make different auth mechanisms. However, I would like to stay with a pretty simple one. Let's make auth with hardcoded Bearer Token.

First, I created auth middlewares folder and file inside: middlewares/auth-middleware.ts. The code of the file is pretty simple.

export async function authMiddleware(ctx, next) {

    // get authorization header
    const authHeader = ctx.request.headers.get('authorization');

    // check if authorization header exists and starts with Bearer
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        ctx.response.status = 401;
        ctx.response.body = {message: 'Unauthorized'};s
        return;
    }

    // get token from authorization header
    const token = authHeader.split(' ')[1];

    // check if token is valid (in this case token is '123456789')
    if (token !== '123456789') {
        ctx.response.status = 403;
        ctx.response.body = {message: 'Forbidden'};
        return;
    }

    // call next middleware
    await next();
}

But to make it work, we must add it to our app object. Let's edit the main.ts file a bit.

...

// import our middleware
import {authMiddleware} from "./middlewares/auth-middleware.ts";

...

// add our middleware to our application
app.use(authMiddleware);

Looks good. Let's test it out.

lorem ipsum api auth works 1

lorem ipsum api auth works 2

lorem ipsum api auth works 3

Everything is fine.

Final?

As you can see, building web apps with Oak framework isn't complex. Honestly, it's the same as Express framework in NodeJS.

I hope today you learned something new and easy can build Deno Rest API by yourself. If you have any questions or want more articles about Deno, please write about it in the comments section.

If you want to have the full source code checkout this GitHub repository.