REST API with Deno

What is Deno, and what are its main features?

Deno is a runtime for JavaScript and TypeScript that is based on the V8 JavaScript engine and the Rust programming language. It was created by Ryan Dahl, the original creator of Node. js, and is focused on security and productivity.

It was built with:

  • Rust (Deno’s core was written in Rust, Node’s in C++)
  • Tokio (the event loop written in Rust)
  • TypeScript (Deno supports both JavaScript and TypeScript out of the box)
  • V8 (Google’s JavaScript runtime used in Chrome and Node, among others)

Still, Deno is under development and it has lots of bugs. But we can build simple REST API Using Deno. And also there are no node_modules in the Deno project.

Here I’m going to show you how to build a simple REST API with CRUD operations with Deno. I'm Not going to use any database and all data store in JSON file.

First of all, we need to install Deno on our computer. you can do that by referring https://deno.land/ according to your operating system.

In windows open the power shell and copy and paste this

iwr https://deno.land/x/install/install.ps1 -useb | iex

command and press enter (it will take some time )

Folder and files Structure

Since there is no node_modules in Deno we need to import Libraries and dependencies from the internet.

Let's build our REST API (Ignore red underlines in screenshots, I did not install Deno extensions in vs code)

First, we create our main file (index.ts)

// oak is pretty much similar to express in nodejs
import { Application } from “https://deno.land/x/oak/mod.ts";
// Import App host and Port from config file
import { APP_HOST, APP_PORT } from “./config/config.ts”;
// Import router from routes file
import router from ‘./routes/routes.ts’;
// Create Application Like Express
const app = new Application()
// Add Routes
app.use(router.routes())
app.use(router.allowedMethods())
// Display App running
console.log(`App Started at Port ${APP_PORT}`)
await app.listen(`${APP_HOST}:${APP_PORT}`)

config/config.ts file

const env = Deno.env();
export const APP_HOST = env.APP_HOST || "127.0.0.1";
export const APP_PORT = env.APP_PORT || 5000;
export const DB_PATH = env.DB_PATH || "./db/todos.json";

models/todos.ts file

export default interface ToDos {
   id: string;
   title: string;
   description: string;
}

routes/routes.ts file

import { Router } from "https://deno.land/x/oak/mod.ts";
import getTodos from './todos/getTodos.ts';
import getTodo from './todos/getTodo.ts';
import addTodo from './todos/addTodos.ts';
import deleteTodo from './todos/deleteTodo.ts';
import editTodo from './todos/editTodo.ts';
const router = new Router()
router.get('/todos', getTodos)
router.get('/todo/:id', getTodo)
router.post('/addTodo', addTodo)
router.put('/editTodo/:id', editTodo)
router.delete('/deleteTodo/:id', deleteTodo)
export default router;

db/todos.json file

[
{
"id":"010fbf79-048e-4508-ac65-5c8b326aa026",
"title":"Learn TypeScript",
"description":"TypeScript is typed version of JavaScript"
},
{
"id":"38bd9aad-4047-47e1-b879-7a9ab77761c5",
"title":"Learn React",
"description":"React is a Front-End web Library"
},
{
"id":"81ded252-661c-4fb1-a1e0-aad717b84891",
"title":"Learn Deno",
"description":"Deno is the next version of nodejs"
}
]

db/db.ts (Where we add and get data from todos.json file) file

import { DB_PATH } from '../config/config.ts';
import ToDos from '../models/todos.ts';
export const getTodosFromJson: () => Promise<ToDos[]> = async () =>{
try {
// Read Files In Deno
const data: any = await Deno.readFile(DB_PATH);
// Decode Data From File
const decode = new TextDecoder()
const decodedData = decode.decode(data)
return JSON.parse(decodedData)
} catch (err) {
console.error(err.message);
}
}
export const writeDataToJson: (todos: ToDos[]) => Promise<void> = async (todos: ToDos[]): Promise<void> => {
try {
// encode Json
const encode = new TextEncoder();
// Write Files in Deno
await Deno.writeFile(DB_PATH, encode.encode(JSON.stringify(todos)))
} catch (err) {
console.error(err.message);
}
}

routes/todos/getTodos.ts file

import { Response } from "https://deno.land/x/oak/mod.ts";
import ToDos from '../../models/todos.ts';
import id from '../../services/createIds.ts'
import { getTodosFromJson } from '../../db/db.ts';
const getTodos = async ({ response }: { response: Response }) => {
try {
const todos: ToDos[] = await getTodosFromJson()
response.body = todos;
} catch (err) {
console.error(err.message);
}
}
export default getTodos

routes/todos/getTodo.ts file

import { Response } from "https://deno.land/x/oak/mod.ts";
import { getTodosFromJson } from '../../db/db.ts';
const getTodo = async ({ params, response }: { params: any, response: Response }) => {
const id = params?.id
if (!id) {
response.status = 400;
response.body = { msg: 'Invalid Id' }
}
const todos = await getTodosFromJson()
const todo = todos.find(todo => todo.id === id)
if (!todo) {
response.status = 404;
response.body = { msg: `No Todo Found on ${id}` }
return;
}
response.status = 200;
response.body = todo;
}
export default getTodo

routes/todos/addTodos.ts file

import { Request, Response } from "https://deno.land/x/oak/mod.ts";
import { v4 as uuid } from "https://deno.land/std/uuid/mod.ts";
import ToDos from '../../models/todos.ts';
import { writeDataToJson, getTodosFromJson } from '../../db/db.ts';
const addTodo = async ({ request, response }: { request: Request, response: Response }) => {
// Check Request Has a body or not
if (!request.hasBody) {
response.status = 400;
response.body = { msg: "Invalid data, Please Add Title and Description" };
return;
}
// Get Title and description from Request
const {
value: { title, description }
} = await request.body();
// Check title and description is valid
if (!title || !description) {
response.status = 422;
response.body = { msg: "Title and Description is required" };
return;
}
// Create New Todo
const newTodo: ToDos = { id: uuid.generate(), title, description }
// Get All Todos
let allTodos: ToDos[] = await getTodosFromJson()
// Add New Todo to allTodos Array
allTodos = [newTodo, ...allTodos]
// Save Data In ToDos.json File
await writeDataToJson(allTodos)
// Response To the Client
response.body = { msg: "New Todo Created", newTodo };
};
export default addTodo

routes/todos/editTodo.ts file

import { Response, Request } from "https://deno.land/x/oak/mod.ts";
import { getTodosFromJson, writeDataToJson } from '../../db/db.ts';
import ToDos from '../../models/todos.ts';
const editTodo = async ({ params, request, response }: { params: any, request: Request, response: Response }): Promise<void> => {
try {
// Save todo id to a variable
const id = params.id;
// Check Request Has a body or not
if (!request.hasBody) {
response.status = 400;
response.body = { msg: "Invalid data, Please Add Title and Description" };
return;
}
// Get Title and description from Request
const {
value: { title, description }
} = await request.body();
// Check title and description is valid
if (!title || !description) {
response.status = 422;
response.body = { msg: "Title and Description is required" };
return;
}
// Get All Todos From Json File And Save it in to a Variable
let allTodos: ToDos[] = await getTodosFromJson();
// Check if todo is here in that Id sent by client
const todo: ToDos | undefined = allTodos.find((todo: ToDos) => todo.id === id)
// check todo is undefined, if so then response 404
if (!todo) {
response.status = 404;
response.body = { msg: `No Todo Found on this ${id} id` }
return;
}
// Add New Title And Description to Old One
todo.title = title;
todo.description = description;
await writeDataToJson(allTodos)
response.status = 200;
response.body = { msg: "Todo has been Edited", todo }
} catch (err) {
console.error(err.message);
}
}
export default editTodo;

routes/todos/deleteTodo.ts file

import { Response } from "https://deno.land/x/oak/mod.ts";
import { getTodosFromJson, writeDataToJson } from '../../db/db.ts';
import ToDos from '../../models/todos.ts';
const deleteTodo = async ({ params, response }: { params: any, response: Response }): Promise<void> => {
try {
// Save todo id to a variable
const id = params.id;
// Get All Todos From Json File And Save it in to a Variable
let allTodos = await getTodosFromJson();
// Check if todo is here in that Id sent by client
const index = allTodos.findIndex((todo: ToDos) => todo.id === id)
// if todo is here index > 0
// if todo is not here index = -1
// check index < 0, if so then response 404
if (index < 0) {
response.status = 404;
response.body = { msg: `No Todo Found on this ${id} id` }
return;
}
// if index > 0 then filter the array and delete todo and save
allTodos = allTodos.filter((todo: ToDos) => todo.id !== id)
await writeDataToJson(allTodos)
response.status = 200;
response.body = { msg: "Todo has been Deleted" }
} catch (err) {
console.error(err.message);
}
}
export default deleteTodo;

Now Our Simple REST API if finished. to run server all we need to do is open the cmd or terminal in that project folder and run

deno -A index.ts

yot will get a message like this. now You can test your REST API using a tool like postman.

Github repo:- https://github.com/Hasi6/REST-API-Using-Deno

That’s it Happy Coding. Thank You.