DEV Community

Cover image for Create your first REST API with deno, oak and mongo [2020]
Ajinkya Devasthali
Ajinkya Devasthali

Posted on • Edited on • Originally published at ajinkyadevasthali.com

Create your first REST API with deno, oak and mongo [2020]

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.

If you are not familiar with deno please read my article. 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. 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

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.

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


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

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

we will connect to a db: test with collection: users.

index.ts

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");
Enter fullscreen mode Exit fullscreen mode

Now we will add our first user and try to print it.

index.ts

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)
Enter fullscreen mode Exit fullscreen mode

run index.ts with following flags :

deno run --allow-net --allow-write --allow-read --allow-plugin --unstable index.ts
Enter fullscreen mode Exit fullscreen mode

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 :

insertRes { $oid: "5ed1391600f90b83009a8d5d" }
findAllRes [ { _id: { $oid: "5ed1391600f90b83009a8d5d" }, name: "Ryan Dahl", age: 39 } ]
findRes { _id: { $oid: "5ed1391600f90b83009a8d5d" }, name: "Ryan Dahl", age: 39 }
Enter fullscreen mode Exit fullscreen mode

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

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)
Enter fullscreen mode Exit fullscreen mode

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

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 }
]
Enter fullscreen mode Exit fullscreen mode

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.

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

//printing respose of delete
console.log('deleteRes2', deleteRes2)
Enter fullscreen mode Exit fullscreen mode

It will print :

deleteRes2 3
findAllRes []
Enter fullscreen mode Exit fullscreen mode

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.

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.

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

.
├── config
│   └── db.ts
├── router.ts
└── server.ts
Enter fullscreen mode Exit fullscreen mode

create a project directory and create a server.ts like following. Please read my comments which oversimplify what the code does.

server.ts

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 })
Enter fullscreen mode Exit fullscreen mode

test the setup by running the following command.

deno run --allow-net server.ts
Enter fullscreen mode Exit fullscreen mode

The command should run without errors and you should see

listening on port 8000
Enter fullscreen mode Exit fullscreen mode

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

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 })
Enter fullscreen mode Exit fullscreen mode

and in router.ts export router instance.

router.ts

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

const router = new Router()

export default router
Enter fullscreen mode Exit fullscreen mode

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. 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

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;
Enter fullscreen mode Exit fullscreen mode

we will now have to use all the flags we used before. Restart the server with following command.

deno run --allow-write --allow-read --allow-plugin --allow-net --allow-env --unstable server.ts
Enter fullscreen mode Exit fullscreen mode

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

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
Enter fullscreen mode Exit fullscreen mode

Such 200, much wow!

POST :

http://localhost:8000/doggos/
Enter fullscreen mode Exit fullscreen mode

and json body will be :

{
    "name": "Max",
    "age": 3,
    "breed": "Shiba Inu"
}
Enter fullscreen mode Exit fullscreen mode

Success for POST : http://localhost:8000/doggos/

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

GET :

http://localhost:8000/doggos/
Enter fullscreen mode Exit fullscreen mode

Success for GET : http://localhost:8000/doggos/

PUT :

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

http://localhost:8000/doggos/5ed2bef600bdbf7900cfdc74
Enter fullscreen mode Exit fullscreen mode

and the json body :

{
    "name": "Max",
    "age": 2,
    "breed": "Pug"
}
Enter fullscreen mode Exit fullscreen mode

Success for PUT : http://localhost:8000/doggos/5ed2bef600bdbf7900cfdc74

DELETE :

finally, delete the record:

http://localhost:8000/doggos/5ed2bef600bdbf7900cfdc74
Enter fullscreen mode Exit fullscreen mode

and it should return 1.

Success

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.

Top comments (0)