Skip to main content

Create REST API using deno

Deno, the creation of Ryan Dahl is a simple, modern, and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust. As Deno 1.0.0 has released, there is much-awaited curiosity amongst developers around working with this new stack.

When it comes to learning a new language, the first thing that comes to our mind is the creation of a todo app, supporting CRUD functionality. So here we will be creating a very simple Todo application using the Deno.

So before starting I assume you have installed Deno on your system, if not then you can find the link here. For the database, we will be using the JSON file as of now to completely focus on creating web API using Deno. Not more words and now let's start.

The file structure we are going to follow is as below

  // Final directory structure
  deno-todo-api/
    --|controllers/
      |--|todo.ts
      |data/
      |--|todos.ts
      |interfaces/
      |--|Todo.ts
      |routes/
      |--|todo.ts
    --|server.ts
  

At any point in time, if you are stuck somewhere, you can follow this link to get the complete code.

  1. Create a directory named deno-todo-api and move into that directory
  mkdir deno-todo-api && cd deno-todo-api
  1. Create the file server.ts in the root of the directory.
  2. Write the following code into the server.ts file to start a server
import { Application, Router } from "https://deno.land/x/oak/mod.ts";

const app = new Application();
const port: number = 8080;

const router = new Router();
router.get("/", ({ response }: { response: any }) => {
  response.body = {
    message: "Rest API tutorial with Deno",
  };
});
app.use(router.routes());
app.use(router.allowedMethods());

console.log('running on port ', port);
await app.listen({ port });

Unlike NodeJS Deno does not need the package.json file for dependency management. Dependency management is simplified by having a list of modules and their respective URLs.

Here we have used oak, a middleware framework for Deno's HTTP server, including a router middleware.

  1. server.ts is the entry point for our web application. To check it run the following command in the root directory
deno run --allow-net server.ts

Open the browser and hit the URL localhost:8080

You can see the following output in your browser

Browser output

  1. Now following the directory structure, we will create following folders in our project with the help of below command
mkdir controllers data interfaces routes
  1. First of all, we will create dummy data to play with our todo APIs.Go to the data folder and create a file called todos.ts and the following code into it
import { v4 } from "https://deno.land/std/uuid/mod.ts";
// interface
import Todo from '../interfaces/Todo.ts';

let todos: Todo[] = [
  {
    id: v4.generate(),
    task: 'Hello world app with Deno',
    done: true,
  },
  {
    id: v4.generate(),
    task: 'Simple Rest API with Deno',
    done: false,
  },
];

export default todos;

We will be using v4 provided by the uuid module to generate unique IDs.

  1. Now create a file in interface/ folder Todo.ts and paste the following code.
export default interface Todo {
  id: string,
  task: string,
  done: boolean,
}

What are the interfaces??

An interface defines the specifications of an entity.

One of TypeScript’s core principles is that type checking focuses on the shape that values have. In TypeScript, interfaces fill the role of naming these types and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

  1. Now create a file named todo.ts in controllers/ directory and paste the following code
import { v4 } from "https://deno.land/std/uuid/mod.ts";
// interfaces
import Todo from "../interfaces/Todo.ts";
// stubs
import todos from "../data/todos.ts";

export default {
  getAll: ({ response }: { response: any }) => {
    response.status = 200;
    response.body = {
      data: todos
    };
  },
  create: async (
    { request, response }: { request: any; response: any },
  ) => {
    const body = await request.body();
    if (!request.hasBody) {
      response.status = 400;
      response.body = {
        message: "Please provide the task detail and  status",
      };
      return;
    }
    let newTodo: Todo = {
      id: v4.generate(),
      task: body.value.todo,
      done: false,
    };
    let data = [...todos, newTodo];
    response.body = {
      data
    };
  },
  getById: (
    { params, response }: { params: { id: string }; response: any },
  ) => {
    const todo: Todo | undefined = todos.find((t) => {
      return t.id === params.id;
    });
    if (!todo) {
      response.status = 404;
      response.body = {
        message: "No task found related to given ID",
      };
      return;
    }
    response.status = 200;
    response.body = {
      data: todo
    };
  },
  update: async (
    { params, request, response }: {
      params: { id: string },
      request: any,
      response: any,
    },
  ) => {
    const todo: Todo | undefined = todos.find((t) => t.id === params.id);
    if (!todo) {
      response.status = 404;
      response.body = {
        message: "No todo found",
      };
      return;
    }

    const body = await request.body();
    const updatedData: { task?: string; done?: boolean } = body.value;

    let newTodos = todos.map((t) => {
      return t.id === params.id ? { ...t, ...updatedData } : t;
    });
    response.status = 200;
    response.body = {
      data: newTodos,
    };
  },
  delete: (
    { params, response }: { params: { id: string }; response: any },
  ) => {
    const allTodos = todos.filter((t) => t.id !== params.id);

    response.status = 200;
    response.body = {
      data: allTodos,
    };
  },
};
  1. Now go to routes and create todo.ts file in it and paste the following code
import { Router } from "https://deno.land/x/oak/mod.ts";

const router = new Router();
// controller
import todoController from "../controllers/todo.ts";

router
  .get("/todos", todoController.getAll)
  .post("/todos", todoController.create)
  .get("/todos/:id", todoController.getById)
  .put("/todos/:id", todoController.update)
  .delete("/todos/:id", todoController.delete);

export default router;
  1. Now since we have created the complete MVC project for our todo APIs, we will be going to update our main server.ts file with following code
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
import todoRouter from "./routes/todo.ts"; // added the router call

const app = new Application();
const port: number = 8080;

app.use(todoRouter.routes()); // Intitialize the router with our application. 
app.use(todoRouter.allowedMethods());

app.addEventListener("listen", ({ secure, hostname, port }) => {
  const protocol = secure ? "https://" : "http://";
  const url = `${protocol}${hostname ?? "localhost"}:${port}`;
  console.log(`Listening on: ${port}`);
});

await app.listen({ port });
  1. Now run again the server with the following command
deno run --allow-net server.ts

Now open the postman and hit the get URL http://localhost:8080/todos/

Browser output

To check the getByID router, copy the ID of any task and hit the URL like

http://localhost:8080/todos/:id

You will be getting output like below

{
    "data": {
        "id": "eeacacdc-e55b-4299-aab7-5f631a2e3975",
        "task": "Hello world app with Deno",
        "done": true
    }
}

Similarly, we can verify the complete crud API created. Below is the reference of all APIs created in the blog post

APIMethodDescription
http://localhost:8080/todos/GETFetch All todos
http://localhost:8080/todos/{id}GETFetch todo by ID
http://localhost:8080/todos/POSTCreate New todo
http://localhost:8080/todos/{id}PUTUpdate todo by ID
http://localhost:8080/todos/{id}DELETEDelete todo by ID

Comments

Popular posts from this blog

4 Ways to Communicate Across Browser Tabs in Realtime

1. Local Storage Events You might have already used LocalStorage, which is accessible across Tabs within the same application origin. But do you know that it also supports events? You can use this feature to communicate across Browser Tabs, where other Tabs will receive the event once the storage is updated. For example, let’s say in one Tab, we execute the following JavaScript code. window.localStorage.setItem("loggedIn", "true"); The other Tabs which listen to the event will receive it, as shown below. window.addEventListener('storage', (event) => { if (event.storageArea != localStorage) return; if (event.key === 'loggedIn') { // Do something with event.newValue } }); 2. Broadcast Channel API The Broadcast Channel API allows communication between Tabs, Windows, Frames, Iframes, and  Web Workers . One Tab can create and post to a channel as follows. const channel = new BroadcastChannel('app-data'); channel.postMessage(data); And oth...

Certbot SSL configuration in ubuntu

  Introduction Let’s Encrypt is a Certificate Authority (CA) that provides an easy way to obtain and install free  TLS/SSL certificates , thereby enabling encrypted HTTPS on web servers. It simplifies the process by providing a software client, Certbot, that attempts to automate most (if not all) of the required steps. Currently, the entire process of obtaining and installing a certificate is fully automated on both Apache and Nginx. In this tutorial, you will use Certbot to obtain a free SSL certificate for Apache on Ubuntu 18.04 and set up your certificate to renew automatically. This tutorial will use a separate Apache virtual host file instead of the default configuration file.  We recommend  creating new Apache virtual host files for each domain because it helps to avoid common mistakes and maintains the default files as a fallback configuration. Prerequisites To follow this tutorial, you will need: One Ubuntu 18.04 server set up by following this  initial ...

Working with Node.js streams

  Introduction Streams are one of the major features that most Node.js applications rely on, especially when handling HTTP requests, reading/writing files, and making socket communications. Streams are very predictable since we can always expect data, error, and end events when using streams. This article will teach Node developers how to use streams to efficiently handle large amounts of data. This is a typical real-world challenge faced by Node developers when they have to deal with a large data source, and it may not be feasible to process this data all at once. This article will cover the following topics: Types of streams When to adopt Node.js streams Batching Composing streams in Node.js Transforming data with transform streams Piping streams Error handling Node.js streams Types of streams The following are four main types of streams in Node.js: Readable streams: The readable stream is responsible for reading data from a source file Writable streams: The writable stream is re...