close
close

Building a simple Todo app with Deno and Oak

In this blog we explore how to build a simple REST API for a Todo application using Deno and the Oak framework. Deno is a secure runtime for JavaScript and TypeScript, and Oak is a middleware framework for Deno’s HTTP server. If you’re familiar with Node.js and Express, you’ll find that Deno and Oak are a similar, yet modern and secure approach to building web applications.

Requirements

Before diving into the code, make sure you have Deno installed on your machine. You can install it by following the official Deno installation guide.

Once installed, you can verify your installation by running:

deno --version
Go to full screen mode

Exit full screen mode

Project setup

  1. Create the project: Start by creating a new folder for your Todo application and navigate within it:
   mkdir deno-app
   cd deno-app
Go to full screen mode

Exit full screen mode

  1. main.ts: This is our main file where the entire server configuration and routes are defined.

Code breakdown

1. Import oak modules

We start importing Application And Router made of oak:

import { Application, Router } from "https://deno.land/x/oak/mod.ts";
Go to full screen mode

Exit full screen mode

  • Application is the core of our API, responsible for handling HTTP requests and responses.
  • Router helps define the API routes.

2. Create the application and router

const app = new Application();
const router = new Router();
Go to full screen mode

Exit full screen mode

  • We initialize the application and router instances, which will be used to define routes and handle incoming HTTP requests.

3. Todo interface and data

To define the structure of our tasks, we create a TypeScript interface:

interface Todo {
  id: string;
  task: string;
}
Go to full screen mode

Exit full screen mode

The tasks are stored in an in-memory array:

let todos: Todo() = ();
Go to full screen mode

Exit full screen mode

4. Middleware for logging and error handling

Deno and Oak let you set up middleware, which is useful for tasks like logging requests and handling errors.

  • Logging middleware: Records the HTTP method and request URL for each request.
  app.use(async (ctx, next) => {
    console.log(`HTTP ${ctx.request.method} on ${ctx.request.url}`);
    await next();
  });
Go to full screen mode

Exit full screen mode

  • Error handling middleware: Catches any errors and sends a generic 500 response if something goes wrong.
  app.use(async (ctx, next) => {
    try {
      await next();
    } catch (err) {
      console.error(err);
      ctx.response.status = 500;
      ctx.response.body = { message: 'Internal Server Error' };
    }
  });
Go to full screen mode

Exit full screen mode

5. Define the routes

We define several RESTful routes for handling CRUD operations on tasks.

  • Carrot route: Simple welcome message.
  router.get('/', (ctx) => {
    ctx.response.body = 'Welcome to the API!';
  });
Go to full screen mode

Exit full screen mode

  • Receive all tasks: Retrieve all tasks.
  router.get("/todos", (ctx) => {
    ctx.response.body = { todos: todos };
  });
Go to full screen mode

Exit full screen mode

  • Create a Todo: Add a new todo item. The new to-do’s id is generated using the current timestamp.
  router.post("/todos", async (ctx) => {
    const data = await ctx.request.body.json();
    const newTodo: Todo = {
      id: new Date().toISOString(),
      task: data.task,
    };

    todos.push(newTodo);
    ctx.response.body = { message: "Created todo!", todo: newTodo };
  });
Go to full screen mode

Exit full screen mode

  • Update a Todo: Modify an existing task with its id.
  router.put("/todos/:todoId", async (ctx) => {
    const tid = ctx.params.todoId;
    const data = await ctx.request.body.json();
    const todoIndex = todos.findIndex((todo) => todo.id === tid);

    if (todoIndex !== -1) {
      todos(todoIndex) = { id: todos(todoIndex).id, task: data.task };
      ctx.response.body = { message: "Updated todo" };
    } else {
      ctx.response.status = 404;
      ctx.response.body = { message: "Todo not found" };
    }
  });
Go to full screen mode

Exit full screen mode

  • Delete a task: Delete one task at a time id.
  router.delete("/todos/:todoId", (ctx) => {
    const tid = ctx.params.todoId;
    const initialLength = todos.length;
    todos = todos.filter((todo) => todo.id !== tid);

    if (todos.length  initialLength) {
      ctx.response.body = { message: "Deleted todo" };
    } else {
      ctx.response.status = 404;
      ctx.response.body = { message: "Todo not found" };
    }
  });
Go to full screen mode

Exit full screen mode

6. Register routes and start the server

Once we have defined the routes, we need to tell the application to use them:

app.use(router.routes());
app.use(router.allowedMethods());
Go to full screen mode

Exit full screen mode

Finally, we start the server on port 3000:

console.log('API is running on http://localhost:3000');
await app.listen({ port: 3000 });
Go to full screen mode

Exit full screen mode

Running the application

To run the application, use the following command:

deno run --allow-net main.ts
Go to full screen mode

Exit full screen mode

  • --allow-net grants network access to the application, which is necessary for Oak to run the HTTP server.

Now open your browser or use an API client like Postman to access the routes:

  • GET http://localhost:3000/todos: Retrieve all tasks.
  • POST http://localhost:3000/todos: Add a new task (send JSON { "task": "Sample task" } in the request text).
  • PUT http://localhost:3000/todos/:todoId: Update an existing task.
  • DELETE http://localhost:3000/todos/:todoId: Delete a task by ID.

Conclusion

In this blog we built a simple Todo API with Deno and Oak. This project highlights how easy it is to create web applications with Deno, thanks to its modern features such as secure permissions and standard TypeScript support. You can extend this app by adding database integration, authentication and more.

Deno offers a fresh perspective on backend development and Oak ensures that the transition from Express to Deno is almost seamless. Give it a try and see how Deno can improve your web development experience!

Happy coding!


Feel free to modify the content as needed. Let me know if you have any questions or need more help. Good luck with your project! 🚀

Explore the code

Visit the GitHub repository to explore the code in detail.