Getting Started with Deno & MongoDB
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.
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
You’ll need to install Deno to get started.
- macOS and Linux Shell:
curl -fsSL https://deno.land/install.sh | sh
- Windows PowerShell:
iwr https://deno.land/install.ps1 -useb | iex
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.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.
Example:
https://data.mongodb-api.com/app/{APP_ID}/endpoint/data/beta

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