Create your first REST API with deno, oak and mongo [2020]
Tags: deno, typescript
Published: 2020-05-31 19:26:21

#What we are building


We are creating a simple REST API with deno. It will be a simple CRUD for a DB collection of dogs. We will connect our deno oak application to mongo using a third party module [deno_mongo](https://deno.land/x/mongo/README.md).

If you are not familiar with deno please read my [article](https://www.ajinkyadevasthali.com/what-is-deno/). In short, It’s the new javascript/ typescript runtime by the original creator of Node. Ryan Dahl started working on deno as he wanted to overcome some of the shortcomings and missed opportunities.

Deno is very new and the ecosystem of third party software is still under development. So it goes without saying we are not going to use any of this in production or anywhere. But we should start playing around with Deno and get to understand its nuances. Dip our toes, if you will.

You will notice, we have to train our minds to put the file extensions at imports, to put additional security params before running program, top-level awaits, etc.

I will suggest you use the official VS code extension by [deno](https://github.com/denoland/vscode_deno). That will take care of the squiggly lines. But make sure to keep it disabled when you are not working with Deno.

Okay, Let’s get to it.

#Tools used 


* [deno](https://deno.land/) 
* [oak](https://deno.land/x/oak) 
* [deno_mongo](https://deno.land/x/mongo/README.md)
* [MongoDB](https://www.mongodb.com/)

#Trying deno_mongo

To connect to MongoDB with our deno app we will be using the third-party module mentioned in the official documentation of deno, i.e, [deno_mongo](https://deno.land/x/mongo/README.md).

While the module is still under development and unstable, it is good enough for the demo of our CRUD.

Although deno_mongo does not have detailed documentation or guides yet  at the time of writing this article, It does give us a working example. So let’s try it out.

This article assumes that you know and will not get into the steps to start a MongoDB instance locally on your machine.

Once you have the MongoDB server running on the standard port, create a file `index.ts`.

#### **`index.ts`**
```typescript

import {MongoClient} from "https://deno.land/x/mongo@v0.7.0/mod.ts";

const client = new MongoClient();
client.connectWithUri("mongodb://localhost:27017");
```
we will connect to a db: `test` with collection: `users`.

#### **`index.ts`**
```typescript
import {MongoClient} from "https://deno.land/x/mongo@v0.7.0/mod.ts";

const client = new MongoClient();
client.connectWithUri("mongodb://localhost:27017");

const db = client.database("test");
const users = db.collection("users");
```
Now we will add our first user and try to print it.

#### **`index.ts`**
```typescript
import { MongoClient } from "https://deno.land/x/mongo@v0.7.0/mod.ts";

const client = new MongoClient();
client.connectWithUri("mongodb://localhost:27017");

const db = client.database("test");
const users = db.collection("users");

// insert (create)
const insertRes = await users.insertOne({
    "name": "Ryan Dahl",
    "age": 39
})

//printing respose of insert
console.log('insertRes', insertRes)


//finding all users (read)
const findAllRes = await users.find({})

console.log('findAllRes', findAllRes)

//finding one user (read)
const findRes = await users.findOne({ _id: insertRes })
/*
or :
const findRes = await users.findOnefindOne({ _id: { "$oid": "<user-id>" } })
*/

console.log('findRes', findRes)
```
run `index.ts` with following flags :

```bash
deno run --allow-net --allow-write --allow-read --allow-plugin --unstable index.ts
```

this should create one user into the database, then fetch all users and then fetch one specific user. You should get a result as follows :

```bash
insertRes { $oid: "5ed1391600f90b83009a8d5d" }
findAllRes [ { _id: { $oid: "5ed1391600f90b83009a8d5d" }, name: "Ryan Dahl", age: 39 } ]
findRes { _id: { $oid: "5ed1391600f90b83009a8d5d" }, name: "Ryan Dahl", age: 39 }
```

that completes the C and the R’s of our CRUD. You may crosscheck the same entry in the DB console or MongoDB compass.

let’s practice more before we start coding our REST API. Let’s try `insertMany`, `updateOne` and `deleteOne` and move on to the next section.

#### **`index.ts`**
```typescript
import { MongoClient } from "https://deno.land/x/mongo@v0.7.0/mod.ts";

const client = new MongoClient();
client.connectWithUri("mongodb://localhost:27017");

const db = client.database("test");
const users = db.collection("users");

// insert Many (create)
const insertRes = await users.insertMany([{
    "name": "Elon Musk",
    "age": 48
},
{
    "name": "Jeff Bezos",
    "age": 56
},
{
    "name": "Mark Zuckerberg",
    "age": 36
},
{
    "name": "Jack Dorsey",
    "age": 33
}])

//printing respose of insert
console.log('insertRes', insertRes)


//finding all users (read)
let findAllRes = await users.find({})
console.log('findAllRes', findAllRes)

//update one user
const updateRes = await users.updateOne(
    { name: "Jack Dorsey" },
    { $set: { age: 43 } },
);

//printing respose of update
console.log('updateRes', updateRes)

//delete one user
const deleteRes = await users.deleteOne({ age: 36 });

//printing respose of delete
console.log('deleteRes', deleteRes)


//finding all users (read)
findAllRes = await users.find({})
console.log('findAllRes', findAllRes)
```

run the same command with all the security flags and you should get following output.

```bash
insertRes [
 { $oid: "5ed142d90049a5e30081036c" },
 { $oid: "5ed142d90091da560081036d" },
 { $oid: "5ed142d90008b49d0081036f" },
 { $oid: "5ed142d900a3ca4d0081036e" }
]
findAllRes [
 { _id: { $oid: "5ed142d90049a5e30081036c" }, name: "Elon Musk", age: 48 },
 { _id: { $oid: "5ed142d90091da560081036d" }, name: "Jeff Bezos", age: 56 },
 { _id: { $oid: "5ed142d900a3ca4d0081036e" }, name: "Mark Zuckerberg", age: 36 },
 { _id: { $oid: "5ed142d90008b49d0081036f" }, name: "Jack Dorsey", age: 33 }
]
updateRes { matchedCount: 1, modifiedCount: 1, upsertedId: null }
deleteRes 1
findAllRes [
 { _id: { $oid: "5ed142d90049a5e30081036c" }, name: "Elon Musk", age: 48 },
 { _id: { $oid: "5ed142d90091da560081036d" }, name: "Jeff Bezos", age: 56 },
 { _id: { $oid: "5ed142d90008b49d0081036f" }, name: "Jack Dorsey", age: 43 }
]
```
Here, output of deleteOne is the number of rows deleted. In case of deleteOne the output can be either 0 or 1. You may try following example of `deleteMany` for clarity.

```typescript
//delete users 
const deleteRes2 = await users.deleteMany({ age: {$gt : 40} });

//printing respose of delete
console.log('deleteRes2', deleteRes2)
```
It will print :

```bash
deleteRes2 3
findAllRes []
```

With U and D complete, we now have a handle on our CRUD. You may head over to docs to see more examples of deno_mongo module [here](https://deno.land/x/mongo/README.md).





Let’s now begin with our REST API…





#Spin up our HTTP server

We will start with spinning up a deno oak server. If you are not familiar please read my article on how to create a simple [HTTP server with Oak](https://www.ajinkyadevasthali.com/deno-simple-basic-server-and-oak-middleware/).

Here’s how the file structure of the app will look like

```
.
├── config
│   └── db.ts
├── router.ts
└── server.ts
```
create a project directory and create a `server.ts` like following. Please read my comments which oversimplify what the code does.

#### **`server.ts`**
```typescript
import { Application } from 'https://deno.land/x/oak/mod.ts';

// we will be listnning on port 8000
const port = 8000;

const app = new Application();

// this is a logger. It will log out every time we make a REST request
app.use(async (ctx, next) => {
    await next();
    const rt = ctx.response.headers.get("X-Response-Time");
    console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});
// this will log the timing of the request 
app.use(async (ctx, next) => {
    const start = Date.now();
    await next();
    const ms = Date.now() - start;
    ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
/*
combined, it will print something like this :
GET http://localhost:8000/doggos/5ed134d6009563a400cf69e0 - 9ms

*/

console.log(`listening on port ${port}`)
app.listen({ port })
```
test the setup by running the following command.

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

The command should run without errors and you should see

```bash
listening on port 8000
```

Once done continue to the next section.

#Setting up router

Now we will set up the router. create a new file `router.ts`

And import in `server.ts` as given below.

#### **`server.ts`**
```typescript
import { Application } from 'https://deno.land/x/oak/mod.ts';
import router from "./router.ts";

const port = 8000;

const app = new Application();

app.use(async (ctx, next) => {
    await next();
    const rt = ctx.response.headers.get("X-Response-Time");
    console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});

app.use(async (ctx, next) => {
    const start = Date.now();
    await next();
    const ms = Date.now() - start;
    ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});

// add these lines
app.use(router.routes())
app.use(router.allowedMethods())



console.log(`listening on port ${port}`)
app.listen({ port })
```

and in `router.ts` export router instance.

#### **`router.ts`**
```typescript
import { Router } from 'https://deno.land/x/oak/mod.ts';

const router = new Router()

export default router
```
Restart the server and check for errors if any. Move on to DB config.

#DB config

We have already had some practice with [deno_mongo](https://deno.land/x/mongo/README.md). In a similar way, we will connect to mongo with DB as `doggos` and the collection also as `doggos`. And we will export this as default as shown below in `config/db.ts`

#### **`db.ts`**
```typescript
import { MongoClient } from "https://deno.land/x/mongo@v0.7.0/mod.ts";

const client = new MongoClient();
client.connectWithUri("mongodb://localhost:27017");

const db = client.database("doggos");
const doggos = db.collection("doggos");

export default doggos;
```
we will now have to use all the flags we used before. Restart the server with following command.

```bash
deno run --allow-write --allow-read --allow-plugin --allow-net --allow-env --unstable server.ts
```

#The “doggo” CRUD

We will now write the routes of our app. These will be the standard CRUD routes.

import doggos exported by `config/db.ts`

`ctx` is the context from which we get the `request` and send `response`

#### **`router.ts`**
```typescript
import { Router } from 'https://deno.land/x/oak/mod.ts';
import doggos from "./config/db.ts"
const router = new Router()

// fetch all doggos
router.get('/doggos', async (ctx) => {
    ctx.response.body = await doggos.find({})
})

// insert a doggo record
router.post('/doggos', async (ctx) => {
    let body: any = await ctx.request.body();
    const { name, age, breed } = body.value;
    ctx.response.body = await doggos.insertOne({
        name,
        age,
        breed
    })
    ctx.response.status = 201;
})

// fetch one doggo by id
router.get('/doggos/:id', async (ctx) => {
    let id: any = ctx.params.id;
    ctx.response.body = await doggos.findOne({ _id: { "$oid": id } })
    ctx.response.status = 200;
})

// update doggo record
router.put('/doggos/:id', async (ctx) => {
    let id: any = ctx.params.id;
    let body: any = await ctx.request.body();
    const { name, age, breed } = body.value;
    ctx.response.body = await doggos.updateOne({ _id: { "$oid": id } }, { $set: { name, age, breed } })
    ctx.response.status = 200;
})

// delete a doggo record
router.delete('/doggos/:id', async (ctx) => {
    let id: any = ctx.params.id;
    ctx.response.body = await doggos.deleteOne({ _id: { "$oid": id } })
    ctx.response.status = 200;
})

export default router
```

#Such 200, much wow!

###POST :

```
http://localhost:8000/doggos/
```

and json body will be :

```json
{
    "name": "Max",
    "age": 3,
    "breed": "Shiba Inu"
}
```

![Success for POST : http://localhost:8000/doggos/](https://www.ajinkyadevasthali.com/wp-content/uploads/2020/05/Screenshot-2020-05-31-at-1.47.36-AM.png)

save this "$oid" as we will need it for the next requests. for me it was `5ed2bef600bdbf7900cfdc74`


###GET :

```
http://localhost:8000/doggos/
```

![Success for GET : http://localhost:8000/doggos/](https://www.ajinkyadevasthali.com/wp-content/uploads/2020/05/Screenshot-2020-05-31-at-1.53.57-AM.png)

###PUT :

we will update the details of Max. Use the Id that you have for Max.

```
http://localhost:8000/doggos/5ed2bef600bdbf7900cfdc74
```

and the json body :

```json
{
    "name": "Max",
    "age": 2,
    "breed": "Pug"
}
```
![Success for PUT : http://localhost:8000/doggos/5ed2bef600bdbf7900cfdc74](https://www.ajinkyadevasthali.com/wp-content/uploads/2020/05/Screenshot-2020-05-31-at-2.04.39-AM.png)

###DELETE :

finally, delete the record:

```
http://localhost:8000/doggos/5ed2bef600bdbf7900cfdc74
```
and it should return 1.

![Success](https://media3.giphy.com/media/WQH7pj43rMzqFuB1xm/giphy.gif)

#Conclusion & Discussion

In Summary, we have seen how we can connect our deno oak server to mongo with the use of deno_mongo. We have had some practice with using the various methods provided by `deno_mongo`. We created a simple server, set up all the routes for our CRUD. We also understand that the `deno_mongo` at the time of writing this article is unstable. Finally we tested our endpoints with postman.

Did you have fun playing around with `Deno`? Are you excited to work with Deno professionally in future?

[Original post on my Blog](https://www.ajinkyadevasthali.com/rest-api-with-deno-oak-and-mongo/).