Making rest API with Deno and TypeScript
Tags: typescript, api, deno, beginners
Published: 2023-07-12 10:41:17

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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. </p>
<!-- /wp:paragraph -->

<!-- wp:more -->
<!--more-->
<!-- /wp:more -->

<!-- wp:heading -->
<h2 class="wp-block-heading">What is Deno?</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>By the way, if you are struggling with which language to learn first, check out my article about that: <a href="https://jakeroid.com/blog/choosing-your-first-programming-language-a-beginner-s-guide/">Choosing Your First Programming Language: A Beginner’s Guide</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Why Deno?</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>As I said before, I love TypeScript. Deno allows to run TypeScript directly without compilation in JavaScript. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>One more reason is more personal. I like all ideas around Deno project. It would be great to try it with you. </p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Our Goal</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Installing Deno</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Creating project</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>I suppose you have installed Deno and are ready to continue. Let's create a new directory for our project and then <code data-enlighter-language="generic" class="EnlighterJSRAW">run deno init</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock {"language":"bash"} -->
<pre class="EnlighterJSRAW" data-enlighter-language="bash" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">mkdir lorem-ipsum-api-deno-typescript
cd lorem-ipsum-api-deno-typescript
deno init</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:image {"id":2862,"width":607,"height":290,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large is-resized"><img src="https://jakeroid.com/wp-content/uploads/2023/02/image-16-1024x491.png" alt="" class="wp-image-2862" width="607" height="290"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Let's try to run a boilerplate. </p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock {"language":"bash"} -->
<pre class="EnlighterJSRAW" data-enlighter-language="bash" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">deno run main.ts</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:image {"id":2865,"width":764,"height":74,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large is-resized"><img src="https://jakeroid.com/wp-content/uploads/2023/02/image-17-1024x99.png" alt="" class="wp-image-2865" width="764" height="74"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Excellent, it's working. Now we are ready to move forward.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Create HTTP end-point</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Let's create HTTP end-point. We will use <a href="https://oakserver.github.io/oak/" target="_blank" rel="noreferrer noopener">Oak framework</a> for that. Oak is similar to Express framework, which you maybe know because it is very popular in NodeJS community.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The basic code could look like the following. </p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock {"language":"typescript"} -->
<pre class="EnlighterJSRAW" data-enlighter-language="typescript" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 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();
}</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>Let's run it and test it. We should add --allow-net parameter to <code data-enlighter-language="generic" class="EnlighterJSRAW">deno run</code> command, because default Deno settings don't allow script access to the networking.</p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">deno run --allow-net main.ts</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:image {"align":"center","id":2869,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-large"><img src="https://jakeroid.com/wp-content/uploads/2023/02/image-18-1024x87.png" alt="" class="wp-image-2869"/></figure>
<!-- /wp:image -->

<!-- wp:image {"id":2870,"width":422,"height":67,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full is-resized"><img src="https://jakeroid.com/wp-content/uploads/2023/02/image-19.png" alt="" class="wp-image-2870" width="422" height="67"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Nice! Everything is working.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Adding router for lorem ipsum generation</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":2874,"width":354,"height":185,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-full is-resized"><img src="https://jakeroid.com/wp-content/uploads/2023/02/image-20.png" alt="" class="wp-image-2874" width="354" height="185"/></figure>
<!-- /wp:image -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 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...";
    }
}</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>Let's import it inside our app.</p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// import our router class
import { LoremIpsumRouter } from "./routes/lorem-ipsum-router.ts";</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>And add using a new router by our app.</p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// create lorem ipsum router
const loremIpsumRouter = new LoremIpsumRouter();

// add handler to our application
app.use(loremIpsumRouter.routes());
app.use(loremIpsumRouter.allowedMethods());</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>Time to run and test.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":2876,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-large"><img src="https://jakeroid.com/wp-content/uploads/2023/02/image-21-1024x100.png" alt="" class="wp-image-2876"/></figure>
<!-- /wp:image -->

<!-- wp:image {"id":2877,"width":590,"height":73,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large is-resized"><img src="https://jakeroid.com/wp-content/uploads/2023/02/image-22-1024x129.png" alt="" class="wp-image-2877" width="590" height="73"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Great! Everything is working fine.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Creating handling functions</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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. </p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">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...";
    }
}</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>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. </p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// 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);
    }
}</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Connect library for generating "lorem ipsum"</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Thankfully for npm packages support in Deno we can use an existing library to generate the text. Let's use <a href="https://www.npmjs.com/package/lorem-ipsum" target="_blank" rel="noreferrer noopener">this package</a>. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In Deno you don't need to install the package. For using lorem ipsum library, let's add those lines inside our controller file. </p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import { LoremIpsum } from 'npm:lorem-ipsum@2.0.8';</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>As you can see, it's easy to use npm package in Deno program. The syntax is easy <code data-enlighter-language="generic" class="EnlighterJSRAW">npm:package-name@version</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Controller code for the responses</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Time to add actual code for generating the response. Here is our complete controller code.</p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">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);
    }
}</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>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. </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Is it working? Of course, I have tested the system.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":2958,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-large"><img src="https://jakeroid.com/wp-content/uploads/2023/07/image-1024x154.png" alt="deno rest api lorem ipsum test" class="wp-image-2958"/></figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Auth for Deno Rest API</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>First, I created auth middlewares folder and file inside: middlewares/auth-middleware.ts. The code of the file is pretty simple. </p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">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();
}</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>But to make it work, we must add it to our app object. Let's edit the main.ts file a bit.</p>
<!-- /wp:paragraph -->

<!-- wp:enlighter/codeblock -->
<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">...

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

...

// add our middleware to our application
app.use(authMiddleware);</pre>
<!-- /wp:enlighter/codeblock -->

<!-- wp:paragraph -->
<p>Looks good. Let's test it out. </p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":2960,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-large"><img src="https://jakeroid.com/wp-content/uploads/2023/07/image-1-1024x220.png" alt="lorem ipsum api auth works 1" class="wp-image-2960"/></figure>
<!-- /wp:image -->

<!-- wp:image {"align":"center","id":2961,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-large"><img src="https://jakeroid.com/wp-content/uploads/2023/07/image-2-1024x231.png" alt="lorem ipsum api auth works 2" class="wp-image-2961"/></figure>
<!-- /wp:image -->

<!-- wp:image {"align":"center","id":2962,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image aligncenter size-large"><img src="https://jakeroid.com/wp-content/uploads/2023/07/image-3-1024x225.png" alt="lorem ipsum api auth works 3" class="wp-image-2962"/></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Everything is fine.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 class="wp-block-heading">Final?</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>As you can see, building web apps with Oak framework isn't complex. Honestly, it's the same as Express framework in NodeJS.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If you want to have the full source code checkout <a href="https://github.com/Jakeroid/blog-article-lorem-ipsum-deno-api" target="_blank" rel="noreferrer noopener">this GitHub repository</a>.</p>
<!-- /wp:paragraph -->