Your First Deno Server in 60 Lines
Tags: deno, typescript, webdev, node
Published: 2020-05-18 13:40:45

---
title: Your First Deno Server in 60 Lines
published: true
description: Today we're going to write our first Deno server in 60 lines.
tags: deno, typescript, webdev, node
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/05w0lpj07dkckwrcjptb.png
canonical_url: https://typeofnan.dev/writing-your-first-deno-server-in-60-lines/
---

Today we're going to write our first Deno server in 60 lines. Deno is self-described as a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust." I'm a huge fan of typescript, so I was really excited to hear about a runtime that treated typescript as a first-class citizen!

### Learn More

If you like this post, consider checking out my [free mailing list](https://buttondown.email/devtuts) and [YouTube tutorials](https://www.youtube.com/c/devtutsco) to learn more JavaScript- and Typescript-related things!

# Getting Started

First, we have to install the runtime. There are a lot of OS-dependent ways to do this, so I'm going to [refer you to the Deno docs](https://deno.land/#installation) to get it installed.

Once Deno is installed, you should be able to type `deno --version` into your command line and see something like this:

```
deno 1.0.0
v8 8.4.300
typescript 3.9.2
```

Deno is young and moving fast, so I wouldn't be surprised if you have a newer version!

# Specifying our Domain

For our first server, let's pretend we're maintaining some sort of virtual bookshelf. Our domain, therefore, deals with books. We can use typescript to specify our `Book` type and create an array of `books` with an initial item. Let's create this file in a new directory and call the file `server.ts`:

**server.ts**

```typescript
type Book = {
  id: number;
  title: string;
  author: string;
};

const books: Book[] = [
  {
    id: 1,
    title: "The Hobbit",
    author: "J. R. R. Tolkien",
  },
];
```

# Grabbing a Server Library

The [oak server library](https://github.com/oakserver/oak) appears to be, thus far, the most ubiquitous server library for deno. Let's use it!

If you're familiar with node, you might think we use an install command and maintain our version in some kind of package.json-like file. Not so! Instead, we specify the package url in our import statement and pin down the version within the import. Deno will first see if we have a cached version of the resource and, if not, will fetch and cache it.

Importantly, note that we specify version 4.0.0 of oak. If we don't specify the version, we'll just get the latest! Seems dangerous given the possibility of breaking changes along the way.

We're going to import `Application` and `Router` from oak. These will create our app server and allow us to configure routes, respectively.

We can add a `get` route to our root url to respond with "Hello world!" We tell our app to listen on port 8000.

```typescript
import { Application, Router } from "https://deno.land/x/oak@v4.0.0/mod.ts";

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

router
  .get("/", (context) => {
    context.response.body = "Hello world!";
  })

app.use(router.routes());

await app.listen({ port: 8000 });
```

This is a functioning server, so we should test it out! In the directory with your file, run the following command:

```
deno run --allow-net server.ts
```

Your app is now listening on port 8000, so you should be able to navigate to `http://localhost:8000` in your browser and see our Hello World example!

# Add Our Routes

We can now add some routes! I will set up some common CRUD routes on our book resource: `get book` to see all books, `get book:id` to see a specific book, and `post book` to create a book.


```typescript
import { Application, Router } from "https://deno.land/x/oak@v4.0.0/mod.ts";

type Book = {
  id: number;
  title: string;
  author: string;
};

const books: Book[] = [
  {
    id: 1,
    title: "The Hobbit",
    author: "J. R. R. Tolkien",
  },
];

const app = new Application();

const router = new Router();

router
  .get("/", (context) => {
    context.response.body = "Hello world!";
  })
  .get("/book", (context) => {
    context.response.body = books;
  })
  .get("/book/:id", (context) => {
    if (context.params && context.params.id) {
      const id = context.params.id;
      context.response.body = books.find((book) => book.id === parseInt(id));
    }
  })
  .post("/book", async (context) => {
    const body = await context.request.body();
    if (!body.value.title || !body.value.author) {
      context.response.status = 400;
      return;
    }
    const newBook: Book = {
      id: 2,
      title: body.value.title,
      author: body.value.author,
    };
    books.push(newBook);
    context.response.status = 201;
  });

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8000 });
```

I think the only bit of this code that might be new or unexplained is `app.use(router.allowedMethods());`. This is simply a handy middleware that will let clients know when a route method is not allowed!

# Final Touch: Logging Middleware

Let's add one final touch: logging middleware that logs how long each request takes:

```typescript
import { Application, Router } from "https://deno.land/x/oak@v4.0.0/mod.ts";

type Book = {
  id: number;
  title: string;
  author: string;
};

const books: Book[] = [
  {
    id: 1,
    title: "The Hobbit",
    author: "J. R. R. Tolkien",
  },
];

const app = new Application();

// Logger
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  console.log(`${ctx.request.method} ${ctx.request.url} - ${ms}ms`);
});

const router = new Router();

router
  .get("/", (context) => {
    context.response.body = "Hello world!";
  })
  .get("/book", (context) => {
    context.response.body = books;
  })
  .get("/book/:id", (context) => {
    if (context.params && context.params.id) {
      let id = context.params.id;
      context.response.body = books.find((book) => book.id === parseInt(id));
    }
  })
  .post("/book", async (context) => {
    const body = await context.request.body();
    if (!body.value.title || !body.value.author) {
      context.response.status = 400;
      return;
    }
    const newBook: Book = {
      id: 2,
      title: body.value.title,
      author: body.value.author,
    };
    books.push(newBook);
    context.response.status = 201;
  });

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8000 });
```

Now whenever you hit our server, the route path and the amount of time it takes to send a response will be logged to the console.

# Fin

And there you have it! Our first Deno server in 60 lines. I'm a huge fan of Deno and looking forward to learning more about it as it evolves. I do have some questions and concerns (for example, given the lack of a lockfile, I'm wondering if and how Deno will allow developers control over indirect dependencies), but for now I'm just enjoying tinkering with this new toy.

### Learn More

If you like this post, consider checking out my [free mailing list](https://buttondown.email/devtuts) and [YouTube tutorials](https://www.youtube.com/c/devtutsco) to learn more JavaScript- and Typescript-related things!