Getting Started with Deno

Learn to Setup Deno and develop a simple Rest API in Deno

In our previous article, we have seen about What is Deno? and what are the features that make it stand apart from Node JS and why it has become a talk of the town in the developers’ community? In this article, you’ll learn to build a simple REST API in Deno.

After the completion of this article, If you follow every step carefully, you will build a simple REST API in Deno which holds student details, can update, delete, search on student details. You’ll learn to install Deno on your system, play with Deno REPL, and create a local server on your system which responds to API calls.

Installation

I will be using macOS to develop this Rest API. To install Deno on macOS, type this command in the terminal, which will install deno using homebrew package manager.

$ brew install deno

You can refer to other operating systems installation guide here.

To verify that deno is successfully installed on your system, Just type command deno --version on your terminal and it will show the installed version of Deno, Chromium’s V8 Engine, and Typescript as Deno has inbuilt support for TypeScript.

$ deno --version
deno 1.0.0
v8 8.4.300
typescript 3.9.2

You can run deno --help to list documentation, options, sub-commands, and environment variables. You can also run deno <subcommand> --help to get information about specific sub-command

Now you can run deno or deno repl to open the REPL (Read-Execute-Print-Loop) and can execute some JavaScript commands.

$ deno repl
Deno 1.0.0
exit using ctrl+d or close()
> console.log("Welcome to CodeHexz")
Welcome to CodeHexz
> a=3
3
> b=5
5
> a+b
8

Running a Deno App

You can use deno run <entry_point> to run any deno app on your system. The entry_point is any .js or .ts file which the deno will download and compile for you to run on your system. Let’s see it live in the terminal:

Type the command deno run https://deno.land/std/examples/welcome.ts . This will download and compile the welcome.ts file from the server and execute it in your terminal. You can check the entry point in your browser; it will display the typescript code.

$ deno run https://deno.land/std/examples/welcome.ts
Download https://deno.land/std/examples/welcome.ts
Warning Implicitly using master branch https://deno.land/std/examples/welcome.ts
Compile https://deno.land/std/examples/welcome.ts
Welcome to Deno 🦕

If you rerun the above command, then it will not download the file again; instead, it will use the cached version of the file. If you have updated the file and want to load the updated version of the file, you need to use the --reload flag

$ deno run https://deno.land/std/examples/welcome.ts
Welcome to Deno 🦕

$ deno run --reload https://deno.land/std/examples/welcome.ts
Download https://deno.land/std/examples/welcome.ts
Warning Implicitly using master branch https://deno.land/std/examples/welcome.ts
Compile https://deno.land/std/examples/welcome.ts
Welcome to Deno 🦕

Deno’s official documentation has different examples to provide clear insight into deno transpilation. You can refer to more examples here.

Building a simple Rest API

We will be developing a simple REST API using Typescript and Oak Framework. We’ll build an API that stores students data and perform CRUD operations with it. We’ll also test its endpoints in Postman App. You can download Postman from here.

Let’s start by creating app.ts file and import the Application and server from https://deno.land/x/oak/mod.ts and setup environments HOST and PORT so that our server can list at a specific HOST and respond to API calls.

import { Application } from 'https://deno.land/x/oak/mod.ts'

const env = Deno.env.toObject()
const HOST = env.HOST || '127.0.0.1'
const PORT = env.PORT || 7700

Now, let’s create router.ts file and import Router from https://deno.land/x/oak/mod.ts and create the routes for our API.

import { Router } from 'https://deno.land/x/oak/mod.ts'
import { getStudents, getStudent, addStudent, updateStudent, deleteStudent } from './controller.ts'

const router = new Router()
router.get('/students', getStudents)
      .get('/students/:roll_no', getStudent)
      .post('/students', addStudent)
      .put('/students/:roll_no', updateStudent)
      .delete('/students/:roll_no', deleteStudent)

export default router

Please note, the methods for the routes aren’t yet created. We will create them later in the controller file.

Now coming back to our app.ts file, Let’s import router and create an application to listen to the HOST and PORT. The final app.ts file would look something like this.

import { Application } from 'https://deno.land/x/oak/mod.ts'
import router from './router.ts'

const env = Deno.env.toObject()
const HOST = env.HOST || '127.0.0.1'
const PORT = env.PORT || 7700

const app = new Application()

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

console.log(`Listening on port ${PORT} ...`)
await app.listen(`${HOST}:${PORT}`)

Now let’s create the controller file named controller.ts which holds the Interface of students and various methods for our REST API. First of all, we will create an interface IStudent and an initial array of students object.

interface IStudent {
  roll_no: string;
  name: string;
  grade: string;
}

let students: Array<IStudent> = [{
  roll_no: "1",
  name: "John",
  grade: "A",
},{
  roll_no: "2",
  name: "Doe",
  grade: "B",
},{
  roll_no: "3",
  name: "Nick",
  grade: "B",
}]

Now we’ll create different methods one by one in controller.ts file and test it using Postman.

getStudents: It returns all the students on the list. As stated in router.ts, Making GET requests on ‘/students’ will call this method.

const getStudents = ({ response }: { response: any }) => { 
  response.body = students 
}

Testing on Postman

getStudent: It returns a student details by its roll_no, or an error message if the student with given roll_no doesn’t exist.

const getStudent = ({ params, response }: { params: { roll_no : string }; response: any }) => {
  const student: IStudent | undefined = searchStudentByRoll(params.roll_no)
  if (student) {
    response.status = 200
    response.body = student
  } else {
    response.status = 404
    response.body = { message: "Student Not Found" }
  }   
}

/* return the student if found and undefined if not */
const searchStudentByRoll = (roll_no: string):( IStudent | undefined ) => students.filter(student => student.roll_no === roll_no)[0]

Testing on Postman

addStudent: Adds a new student on the list when POST request is triggered on ‘/students’. We need to provide new student data in the body section of the request.

const addStudent = async ({ request, response }: { request: any; response: any }) => {
  const body = await request.body()
  const student: IStudent = body.value  
  students.push(student)
  response.body = { message: 'Student Added Successfully' }
  response.status = 200
}

Testing on Postman

updateStudent: Updates a student with provided roll_no. It returns an error if the student doesn’t exist. This method is called when a PUT request is made on ‘/students’ with valid roll_no.

const updateStudent = async ({ params, request, response }: { params: { roll_no: string }; request: any; response: any }) => {
  let student: IStudent | undefined = searchStudentByRoll(params.roll_no)
  if (student) {
    const body = await request.body()
    const updateInfos: { name?: string; grade?: string } = body.value
    student = { ...student, ...updateInfos}
    students = [...students.filter(student => student.roll_no !== params.roll_no), student]
    response.status = 200
    response.body = { message: "Student details Updated Successfully!" }
  } else {
    response.status = 404
    response.body = { message: "Student Not found" }
  }  
}

deleteStudent: Deletes a student with provided roll_no if found. This method is called when a DELETE request is made.

const deleteStudent = ({ params, response }: { params: { roll_no: string }; response: any }) => {
  students = students.filter(student => student.roll_no !== params.roll_no)
  response.body = { message: "Student deleted successfully" }
  response.status = 200
}

After creating these functions in the controller file, we need to import to the router file so we can call them for specific routes. Complete the controller.ts by exporting these functions.

export { getBooks, getBook, addBook, updateBook, deleteBook }

Troubleshooting

While running the app.ts file in the terminal with the command deno run app.ts, you may get environment variables or network error due to security features. So you need to specify flags while running the file as below.

$ deno run --allow-env --allow-net app.ts
Listening on port 7700 ...

If you want to learn more about Deno, Here are some resources: Deno’s official website, The API documentation, and you can find a lot more resources, libraries, and frameworks in the Deno’s awesome list.

Ending the Article

So here is the quick guide to get you through the Deno installation and develop a simple Rest API using Deno. If you are Node.JS developer and love typescript, then this thing is undoubtedly for you, where you also get security in your applications.

Find full source code on Github here

2 COMMENTS

  1. Hi, I think there is an issue in app.ts, you’re using await in the last line and it’s not wrapped in async function

LEAVE A REPLY

Please enter your comment!
Please enter your name here