Full Deno + MongoDB REST API Project Breakdown sing npm:mongodb and Oak (Step-by-Step)
4 min readApr 16, 2025
✅ 1. Project Setup
📄 deno.json or deno.jsonc
This config file lets you run your app using deno task dev:
{
"tasks": {
"dev": "deno run --allow-net --allow-env --unstable --compat mod.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1",
"oak": "https://deno.land/x/oak@v12.6.0/mod.ts",
"mongodb": "npm:mongodb@6.3.0"
}
}📁 2. Folder Structure
deno-mongo-api/
│
├── mod.ts # Main server file
├── deps.ts # Central imports
│
├── db/
│ └── connect.ts # MongoDB connection
│
├── models/
│ └── user.ts # MongoDB schema/model
│
├── controllers/
│ └── userController.ts # All logic for user routes
│
└── routes/
└── userRoutes.ts # Defines API routes🔗 3. deps.ts
Why? Centralize imports so you can manage versions in one place.
export { Application, Router, Context } from "oak";
export { MongoClient, ObjectId } from "mongodb";🌐 4. mod.ts
Starts the server and adds middleware
import { Application } from "./deps.ts";
import router from "./routes/userRoutes.ts";
const app = new Application();
// Use router
app.use(router.routes());
app.use(router.allowedMethods());
console.log("🚀 Server running at http://localhost:8000");
await app.listen({ port: 8000 });🧵 5. db/connect.ts
Connects to MongoDB
import { MongoClient } from "../deps.ts";
const client = new MongoClient("mongodb://127.0.0.1:27017");
await client.connect();
const db = client.db("deno_api");
console.log("✅ Connected to MongoDB");
export default db;client.connect()→ connects to MongoDBdb("deno_api")→ selects database
📄 6. models/user.ts
Defines a TypeScript interface + collection
import db from "../db/connect.ts";
import { ObjectId } from "../deps.ts";
export interface UserSchema {
_id: ObjectId;
name: string;
email: string;
}
const users = db.collection<UserSchema>("users");
export default users;- Declares schema fields:
_id,name,email - Gets the
userscollection
🧠 7. controllers/userController.ts
Handles API logic (CRUD)
import { Context, ObjectId } from "../deps.ts";
import users from "../models/user.ts";
interface User {
name: string;
email: string;
}
function isKnownError(error: unknown): error is Error {
return error instanceof Error;
}Get Deep Rai’s stories in your inbox
Join Medium for free to get updates from this writer.
➕ createUser
export const createUser = async (ctx: Context) => {
try {
const { name, email }: User = await ctx.request.body({ type: "json" }).value;
if (!name || !email) {
ctx.response.status = 400;
ctx.response.body = { message: "Name and email are required" };
return;
}
const newId = await users.insertOne({ name, email });
ctx.response.status = 201;
ctx.response.body = { message: "User created", id: newId };
} catch (error) {
ctx.response.status = 500;
ctx.response.body = isKnownError(error)
? { message: "Internal Server Error", error: error.message }
: { message: "Unknown error occurred" };
}
};📥 getUsers
export const getUsers = async (ctx: Context) => {
try {
const allUsers = await users.find({}).toArray();
ctx.response.body = allUsers;
} catch (error) {
ctx.response.status = 500;
ctx.response.body = isKnownError(error)
? { message: "Internal Server Error", error: error.message }
: { message: "Unknown error occurred" };
}
};📥 getUser
export const getUser = async (ctx: Context) => {
try {
const id = ctx.params.id!;
const user = await users.findOne({ _id: new ObjectId(id) });
if (user) {
ctx.response.body = user;
} else {
ctx.response.status = 404;
ctx.response.body = { message: "User not found" };
}
} catch (error) {
ctx.response.status = 500;
ctx.response.body = isKnownError(error)
? { message: "Internal Server Error", error: error.message }
: { message: "Unknown error occurred" };
}
};✏️ updateUser
export const updateUser = async (ctx: Context) => {
try {
const id = ctx.params.id!;
const { name, email }: User = await ctx.request.body({ type: "json" }).value;
if (!name || !email) {
ctx.response.status = 400;
ctx.response.body = { message: "Name and email are required" };
return;
}
const result = await users.updateOne(
{ _id: new ObjectId(id) },
{ $set: { name, email } }
);
ctx.response.body = { message: "User updated", modifiedCount: result.modifiedCount };
} catch (error) {
ctx.response.status = 500;
ctx.response.body = isKnownError(error)
? { message: "Internal Server Error", error: error.message }
: { message: "Unknown error occurred" };
}
};❌ deleteUser
export const deleteUser = async (ctx: Context) => {
try {
const id = ctx.params.id!;
const result = await users.deleteOne({ _id: new ObjectId(id) });
ctx.response.body = { message: "User deleted", deletedCount: result.deletedCount };
} catch (error) {
ctx.response.status = 500;
ctx.response.body = isKnownError(error)
? { message: "Internal Server Error", error: error.message }
: { message: "Unknown error occurred" };
}
};🌐 8. routes/userRoutes.ts
Maps URLs to controller functions
import { Router } from "../deps.ts";
import {
getUsers,
getUser,
createUser,
updateUser,
deleteUser,
} from "../controllers/userController.ts";
const router = new Router();
router
.get("/users", getUsers)
.get("/users/:id", getUser)
.post("/users", createUser)
.put("/users/:id", updateUser)
.delete("/users/:id", deleteUser);
export default router;✅ Test Your API
Use curl or Postman:
- Create User
curl -X POST http://localhost:8000/users \
-H "Content-Type: application/json" \
-d '{"name":"John", "email":"john@example.com"}'- Get All Users
curl http://localhost:8000/users- Update User
curl -X PUT http://localhost:8000/users/<id> \
-H "Content-Type: application/json" \
-d '{"name":"Johnny", "email":"new@example.com"}'- Delete User
curl -X DELETE http://localhost:8000/users/<id>🔚 Summary
✅ You now have a full working REST API in Deno using:
- Oak for server & routing
- npm MongoDB driver (modern way to use Mongo)
- Modular structure (clean separation of concerns)








