Building a Simple Todo App with Deno and Oak

Manthan Ankolekar - Oct 11 - - Dev Community

In this blog, we’ll 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 Deno and Oak to be a similar yet modern and secure approach to building web applications.

Prerequisites

Before diving into the code, ensure that 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
Enter fullscreen mode Exit fullscreen mode

Project Setup

  1. Creating the Project: Start by creating a new directory for your Todo application and navigate into it:
   mkdir deno-app
   cd deno-app
Enter fullscreen mode Exit fullscreen mode
  1. main.ts: This is our main file where the entire server setup and routes are defined.

Code Breakdown

1. Importing Oak Modules

We start by importing Application and Router from Oak:

import { Application, Router } from "https://deno.land/x/oak/mod.ts";
Enter fullscreen mode Exit fullscreen mode
  • Application is the core of our API, responsible for handling HTTP requests and responses.
  • Router helps define the API routes.

2. Creating the Application and Router

const app = new Application();
const router = new Router();
Enter fullscreen mode Exit fullscreen 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 todos, we create a TypeScript interface:

interface Todo {
  id: string;
  task: string;
}
Enter fullscreen mode Exit fullscreen mode

The todos will be stored in an in-memory array:

let todos: Todo[] = [];
Enter fullscreen mode Exit fullscreen mode

4. Middleware for Logging and Error Handling

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

  • Logging Middleware: Logs 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();
  });
Enter fullscreen mode Exit fullscreen 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' };
    }
  });
Enter fullscreen mode Exit fullscreen mode

5. Defining the Routes

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

  • Root Route: Simple welcome message.
  router.get('/', (ctx) => {
    ctx.response.body = 'Welcome to the API!';
  });
Enter fullscreen mode Exit fullscreen mode
  • Get All Todos: Fetch all the todos.
  router.get("/todos", (ctx) => {
    ctx.response.body = { todos: todos };
  });
Enter fullscreen mode Exit fullscreen mode
  • Create a Todo: Add a new todo item. The new todo'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 };
  });
Enter fullscreen mode Exit fullscreen mode
  • Update a Todo: Modify an existing todo by 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" };
    }
  });
Enter fullscreen mode Exit fullscreen mode
  • Delete a Todo: Remove a todo by its 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" };
    }
  });
Enter fullscreen mode Exit fullscreen mode

6. Registering Routes and Starting the Server

After defining the routes, we need to tell the application to use them:

app.use(router.routes());
app.use(router.allowedMethods());
Enter fullscreen mode Exit fullscreen mode

Finally, we start the server on port 3000:

console.log('API is running on http://localhost:3000');
await app.listen({ port: 3000 });
Enter fullscreen mode Exit fullscreen mode

Running the Application

To run the application, use the following command:

deno run --allow-net main.ts
Enter fullscreen mode Exit fullscreen 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 todos.
  • POST http://localhost:3000/todos: Add a new todo (send JSON { "task": "Sample task" } in the request body).
  • PUT http://localhost:3000/todos/:todoId: Update an existing todo.
  • DELETE http://localhost:3000/todos/:todoId: Delete a todo by ID.

Conclusion

In this blog, we’ve built a simple Todo API using Deno and Oak. This project highlights how easy it is to create web applications with Deno, thanks to its modern features like secure permissions and TypeScript support out of the box. You can expand on this app by adding database integration, authentication, and more.

Deno provides a fresh take on backend development, and Oak makes the transition from Express to Deno almost seamless. Give it a try and explore how Deno can improve your web development experience!

Happy coding!


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

Exploring the Code

Visit the GitHub repository to explore the code in detail.


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .