MongoDB Developer Centerchevron-right
Developer Topicschevron-right

Getting Started with Deno & MongoDB

Jesse HallPublished Jan 21, 2022 • Updated May 12, 2022
AtlasData APITypeScriptRust
Copy Link
facebook icontwitter iconlinkedin icon
random alt
Rate this quickstart
Deno is a “modern” runtime for JavaScript and TypeScript that is built in Rust. This makes it very fast!
If you are familiar with Node.js then you will be right at home with Deno. It is very similar but has some improvements over Node.js. In fact, the creator of Deno, Ryan Dahl, also created Node and Deno is meant to be the successor to Node.js.
💡 Fun Fact: Deno is an anagram. Rearrange the letters in Node to spell Deno.
Deno has no package manager, uses ES modules, has first-class await, has built-in testing, and is somewhat browser-compatible by providing built-in fetch and the global window object.
Aside from that, it’s also very secure. It’s completely locked down by default and requires you to enable each access method specifically.
This makes Deno pair nicely with MongoDB since it is also super secure by default.
Video Version
Here is a video version of this article if you prefer to watch.


  • TypeScript — Deno uses TypeScript by default, so some TypeScript knowledge is needed.
  • Basic MongoDB knowledge
  • Understanding of RESTful APIs

Getting Deno set up

You’ll need to install Deno to get started.
  • macOS and Linux Shell:
    curl -fsSL | sh
  • Windows PowerShell:
    iwr -useb | iex
For more options, here are the Deno installation instructions.

Setting up middleware and routing

For this tutorial, we’re going to use Oak, a middleware framework for Deno. This will provide routing for our various app endpoints to perform CRUD operations.
We’ll start by creating a server.ts file and import the Oak Application method.
💡 If you are familiar with Node.js, you’ll notice that Deno does things a bit differently. Instead of using a package.json file and downloading all of the packages into the project directory, Deno uses file paths or URLs to reference module imports. Modules do get downloaded and cached locally, but this is done globally and not per project. This eliminates a lot of the bloat that is inherent from Node.js and its node_modules folder.
Next, let’s start up the server.
We’re going to create our routes in a new file named routes.ts. This time, we’ll import the Router method from Oak. Then, create a new instance of Router() and export it.
Now, let’s bring our router into our server.ts file.

Setting up the MongoDB Data API

In most tutorials, you’ll find that they use the mongo third-party Deno module. For this tutorial, we’ll use the brand new MongoDB Atlas Data API to interact with our MongoDB Atlas database in Deno. The Data API doesn’t require any drivers!
Let’s set up our MongoDB Atlas Data API. You’ll need a MongoDB Atlas account. If you already have one, sign in, or register now.
From the MongoDB Atlas Dashboard, click on the Data API option. By default, all MongoDB Atlas clusters have the Data API turned off. Let’s enable it for our cluster. You can turn it back off at any time.
After enabling the Data API, we’ll need to create an API Key. You can name your key anything you want. I’ll name mine data-api-test. Be sure to copy your API key secret at this point. You won’t see it again!
Also, take note of your App ID. It can be found in your URL Endpoint for the Data API.

Configuring each route

At this point, we need to set up each function for each route. These will be responsible for Creating, Reading, Updating, and Deleting (CRUD) documents in our MongoDB database.
Let’s create a new folder called controllers and a file within it called todos.ts.
Next, we’ll set up our environmental variables to keep our secrets safe. For this, we’ll use a module called dotenv.
Here, we are importing the config method from that module and then using it to get our DATA_API_KEY and APP_ID environmental variables. Those will be pulled from another file that we’ll create in the root of our project called .env. Just the extension and no file name.
This is a plain text file that allows you to store secrets that you don’t want to be uploaded to GitHub or shown in your code. To ensure that these don’t get uploaded to GitHub, we’ll create another file in the root of our project called .gitignore. Again, just the extension with no name.
In this file, we’ll simply enter .env. This lets git know to ignore this file so that it’s not tracked.
Now, back to the todos.ts file. We’ll configure some variables that will be used throughout each function.
We’ll set up our base URI to our MongoDB Data API endpoint. This will utilize our App ID. Then we need to define our data source, database, and collection. These would be specific to your use case. And lastly, we will define our fetch options, passing in our Data API key.

Create route

Now we can finally start creating our first route function. We’ll call this function addTodo. This function will add a new todo item to our database collection.
This function will accept a request and response. If the request doesn’t have a body it will return an error. Otherwise, we’ll get the todo from the body of the request and use the insertOne Data API endpoint to insert a new document into our database.
We do this by creating a query that defines our dataSource, database, collection, and the document we are adding. This gets stringified and sent using fetch. fetch happens to be built into Deno as well; no need for another module like in Node.js.
We also wrap the entire function contents with a try..catch to let us know if there are any errors.
As long as everything goes smoothly, we’ll return a status of 201 and a response.body.
Lastly, we’ll export this function to be used in our routes.ts file. So, let’s do that next.
Testing the create route
Let’s test our create route. To start the server, we’ll run the following command:
deno run --allow-net --allow-read server.ts
💡 Like mentioned before, Deno is locked down by default. We have to specify that network access is okay by using the --allow-net flag, and that read access to our project directory is okay to read our environmental variables using the --allow-read flag.
Now our server should be listening on port 3000. To test, we can use Postman, Insomnia, or my favorite, the Thunder Client extension in VS Code.
We’ll make a POST request to localhost:3000/api/todos and include in the body of our request the json document that we want to add.
💡 Normally, I would not create an ID manually. I would rely on the MongoDB generated ObjectID, _id. That would require adding another Deno module to this project to convert the BSON ObjectId. I wanted to keep this tutorial as simple as possible.
If all goes well, we should receive a successful response.

Read all documents route

Now let’s move on to the read routes. We’ll start with a route that gets all of our todos called getTodos.
This one will be directed to the find Data API endpoint. We will not pass anything other than the dataSource, database, and collection into our query. This will return all documents from the specified collection.
Next, we’ll need to add this function into our exports at the bottom of the file.
Then we’ll add this function and route into our routes.ts file as well.
Testing the read all documents route
Since we’ve made changes, we’ll need to restart our server using the same command as before:
deno run --allow-net --allow-read server.ts
To test this route, we’ll send a GET request to localhost:3000/api/todos this time, with nothing in our request body.
This time, we should see the first document that we inserted in our response.

Read a single document route

Next, we’ll set up our function to read a single document. We’ll call this one getTodo.
This function will utilize the findOne Data API endpoint and we’ll pass a filter this time into our query.
We’re going to use query params from our URL to get the ID of the document we will filter for.
Next, we need to export this function as well.
And, we’ll import the function and set up our route in the routes.ts file.
Testing the read single document route
Remember to restart the server. This route is very similar to the “read all documents” route. This time, we will need to add an ID to our URL. Let’s use: localhost:3000/api/todos/1.
We should see the document with the todoId of 1.
💡 To further test, try adding more test documents using the POST method and then run the two GET methods again to see the results.

Update route

Now that we have documents, let’s set up our update route to allow us to make changes to existing documents. We’ll call this function updateTodo.
This route will accept three arguments: params, request, and response. The params will tell us which document to update, and the request will tell us what to update.
We’ll use the updateOne Data API endpoint and set a filter and update in our query.
The filter will indicate which document we are updating and the update will use the $set operator to update the document fields.
The updated data will come from our request.body.
Let’s export this function at the bottom of the file.
And, we’ll import the function and set up our route in the routes.ts file.
This route will use the PUT method.
Testing the update route
Remember to restart the server. To test this route, we’ll use a combination of the previous tests.
Our method will be PUT. Our URL will be localhost:3000/api/todos/1. And we’ll include a json document in our body with the updated fields.
Our response this time will indicate if a document was found, or matched, and if a modification was made. Here we see that both are true.
If we run a GET request on that same URL we’ll see that the document was updated!

Delete route

Next, we'll set up our delete route. We’ll call this one deleteTodo.
This route will use the deleteOne Data API endpoint and will filter using the URL params.
Let’s export this function.
And we’ll import it and set up its route in the routes.ts file.
Testing the delete route
Remember to restart the server. This test will use the DELETE method. We’ll delete the first todo using this URL: localhost:3000/api/todos/1.
Our response will indicate how many documents were deleted. In this case, we should see that one was deleted.

Bonus: Aggregation route

We're going to create one more bonus route. This one will demonstrate a basic aggregation pipeline using the MongoDB Atlas Data API. We'll call this one getIncompleteTodos.
For this route, we'll use the aggregate Data API endpoint. This endpoint will accept a pipeline.
We can pass any aggregation pipeline through this endpoint. Our example will be basic. The result will be a count of the incomplete todos.
Let’s export this final function.
And we’ll import it and set up our final route in the routes.ts file.
Testing the aggregation route
Remember to restart the server. This test will use the GET method and this URL: localhost:3000/api/todos/incomplete/count.
Add a few test todos to the database and mark some as complete and some as incomplete.
Our response shows the count of incomplete todos.


We created a Deno server that uses the MongoDB Atlas Data API to Create, Read, Update, and Delete (CRUD) documents in our MongoDB database. We added a bonus route to demonstrate using an aggregation pipeline with the MongoDB Atlas Data API. What next?
If you would like to see the completed code, you can find it here. You should be able to use this as a starter for your next project and modify it to meet your needs.
I’d love to hear your feedback or questions. Let’s chat in the MongoDB Community.

Copy Link
facebook icontwitter iconlinkedin icon
Rate this quickstart
Structuring Data with Serde in Rust

May 12, 2022
How Prisma Introspects a Schema from a MongoDB Database

May 19, 2022
Using Rust Web Development Frameworks with MongoDB

Aug 30, 2022
Code Example
Beginner Coding Project: Build a Blog Engine with Rust and MongoDB

Jun 11, 2022
Table of Contents