Create a simple RESTful API with Deno & MongoDB - Pt.1

Why you should try Deno

Like Node.js, Deno is intended to provide a platform for executing JavaScript apart from the browser and is not a complete self-implementation, but is based on some existing projects. In contrast to Node.js, whose core is written in C++, Deno uses Rust as programming language while both use Google's v8 JavaScript engine.

Deno wants to distinguish itself positively from its predecessor with the following aspects:

  • It is secure by default: Access to files, network or environment needs to be explicitly enabled
  • It has built-in TypeScript support
  • Packages are decentralized : Packages are imported from URLs
  • It has built-in testing support
  • It has as a set of reviewed standard modules that are guaranteed to work with Deno
  • It implements top-level await
  • It ships a single executable file


Deno works on macOS, Linux, and Windows. It is constantly getting updated and updates often include breaking changes. This article is based on Deno v1.3.1.
Depending on the operating system, Deno can be installed using one of the following commands:

Using Shell (macOS and Linux):

curl -fsSL | sh

Using PowerShell (Windows):

iwr -useb | iex

Using Chocolatey (Windows):

choco install deno

Using Homebrew (macOS):

brew install deno

More information about the installation process can be found in the official Deno manual

Create our first Deno application

We are going to use oak as a middleware framework for Deno's http server, including a router middleware.

In an new, empty folder we create a file called server.ts as an entry point for our application. <br>
To use oak's Router and Application class we need to first import it on top of our file:

import { Application, Router, RouterContext } from "";

We then need to initialize our app and the router by instantiating the respecting classes . We also specify the port we want to use as a variable :

const port = 8000;
const app = new Application();
const router = new Router();

The Application class wraps the serve() function from the http package and has two methods: use() and listen(). We need the listen() method to start our server on the port we specified and to start processing requests with the registered middleware. The router middleware is added with the use() method. To know if the server is running we also add a console log:


console.log(`Server running on port ${port}`);

await app.listen({ port });

Now we added the router middleware and allowed all methods for now but we didn't specify any routes. We'll start with a route for a simple GET request to the url '/user', which is later used to get a single user from the database. After the url string we add a function the be executed when a request is made to that url. We accept the RouterContext as the function's argument and for now we simply return a string 'User1' as the response body of that RouterContext.

router.get('/user', (ctx: RouterContext) => {
  ctx.response.body = 'User1';

This is everything we need for our first 'Hello world'-like application. To start the server we need to run the server.ts file. But remember that Deno is secure by default. This means, unless you specifically enable it, a deno module has no access to things like files or the network. Because we need network access for our application we need to grant permission when starting it. To start the server with network access type in the following command:

deno run --allow-net server.ts  

To test if everything is setup correctly we can now make a GET request to http://localhost:8000/user.
You can use tools like Postman or a plugin for your favourite IDE (like REST Client for VSCode). The answer from your server should then look like this:


Refactoring for better structure

To have a better project structure from the beginning and make things easier when the application grows, we'll create a new file for dependeny-management and one for the router.

We start with creating a new file called deps.ts where we import and re-export all external libraries in one central place. This isn't necessary for such a simple application but if we would import from (multiple) libraries into multiple files this would help us keep the libraries on the same version without having to check in every file. So let's import and export oak's Router and Application again:

export { Application, Router, RouterContext } from "";

Next we'll move the router into a seperate file, therefore we create a new file called router.ts.

First, we import oak's Router from our newly created deps.ts file and initialize it (make sure to remove it in the server.ts file). Then we move our first created GET route from the server.ts file to our router.ts file and export the router, so we can import it anywhere in our application:

import { Router, RouterContext } from "";

const router = new Router();

router.get('/user', (ctx: RouterContext) => {
  ctx.response.body = 'User1';

export default router;

To finish things off we need to update the imports in our server.ts file to our newly created components. The complete file will now look like this:

import { Application } from "./deps.ts";
import router from "./router.ts";

const port = 8000;
const app = new Application();


console.log(`Server running on port ${port}`);

await app.listen({ port });

Our application should now behave like before. To test this we can restart it and make a GET request to http://localhost:8000/user.

That's it for part one. We set up a little demo application with a simple route using the oak framework. In the next parts we are going to expand this Deno API with multiple routes and requests, user registration and user login with JWT authentication based on a MongoDB.

The header image is by hashrock and can be found at