What is Deno's Oak Framework?

overview

Oak is Deno’s web framework, designed to help developers create efficient web applications.

Key features of Oak include routing, request and response processing, and middleware support. Also, the use of middleware makes it easy for developers to extend its functionality.

Here, we will introduce the basic usage of Oak using the following versions.

Oak v9.0.0
Deno v1.18.0

Basic Oak Concepts

Routing

Routing is a mechanism for receiving HTTP requests from clients and processing them accordingly. Oak allows you to use its routing capabilities to properly handle requests for different URL paths and HTTP methods.

import { Application, Router } from "https://deno.land/x/oak/mod.ts";

const router = new Router();

const app = new Application();

// define route for GET method
router.get('/', (context) => {
  context.response.body = 'Hello World! ';
});

// Define route for POST method
router.post('/users', async (context) => {
  const { value: { name, email } } = await context.request.body();
  // process user data and return response
  context.response.body = { message: `User created: ${name} (${email})` };
});

// route definition with parameters
router.get('/users/:id', (context) => {
  const userId = context.params.id;
  // get user data from a database or other source
  const user = { id: userId, name: 'Taro Yamada' };
  context.response.body = user;
});

app.use(router.routes());

// start the server
app.listen({ port: 3000 });
console

.log('Server started on port 3000');

Middleware

Oak allows you to create middleware and implement processing on a per-request basis.

import { Application, Router } from "https://deno.land/x/oak/mod.ts";

const router = new Router();
const app = new Application();

app. use(async (context, next) => {
  console.log(`${context.request.method} ${context.request.url}`);
  await next();
});

// define route for GET method
router.get('/', (context) => {
  context.response.body = 'Hello world! ';
});

// Define route for POST method
router.post('/users', async (context) => {
  const { value: { name, email } } = await context.request.body();
  if (!name || !email) {
    context.response.status = 400;
    context.response.body = 'Name and email are required';
  } else {
    // store and process data
    saveUserData(name, email);
    context.response.body = { message: `User created: ${name} (${email})` };
  }
});

// route definition with parameters
router.get('/users/:id', (context) => {
  const userId = context.params.id;
  // get user data from a database or other source
  const user = { id: userId, name: 'Taro Yamada' };
  context.response.body = user;
});

app.use(router.routes());

// start the server
app.listen({ port: 3000 });
console.log('Server started on port 3000');

// Example of an asynchronous function that retrieves data from the database
async function getDataFromDatabase() {
  // Execute database access asynchronous processing
  return await db.query('SELECT * FROM users');
}

// Example of processing to save user data
function saveUserData(name, email) {
  // data save processing
  // ...
}

This example makes use of the Oak framework. It uses Router for routing and Application for application management. Middleware makes use of the app.use() method.

Handler

In Oak, handler functions define what to do with routed requests. A handler is responsible for receiving a request, performing the necessary processing, and returning a response.

Below is an example of a handler in Deno’s Oak.

import { Application, Router } from "https://deno.land/x/oak/mod.ts";

const router = new Router();
const app = new Application();

// define a handler for the GET method
router. get('/', async (context) => {
  try {
    // Execute an asynchronous function to process the request
    const data = await getDataFromDatabase();
    context.response.body = data;
  } catch (error) {
    console.error('An error has occurred:', error);
    context.response.status = 500;
    context.response.body = 'An error has occurred';
  }
});

// define handler for POST method
router.post('/users', async (context) => {
  const { value: { name, email } } = await context.request.body();
  // process user data
  if (!name || !email) {
    context.response.status = 400;
    context.response.body = 'Name and email are required';
  } else {
    // store and process data
    saveUserData(name, email);
    context.response.body = { message: `User created: ${name} (${email})` };
  }
});

app.use(router.routes());

// start the server
app.listen({ port: 3000 });
console.log('Server started on port 3000');

// Example of an asynchronous function that retrieves data from the database
async function getDataFromDatabase() {
  // Execute database access asynchronous processing
  return await db.query('SELECT * FROM users');
}

// Example of processing to save user data
function saveUserData(name, email) {
  // data save processing
  // ...
}

The example above defines handlers for the GET and POST methods. The GET method handler uses an asynchronous function to retrieve data from the database and return it as a response. The POST method handler obtains the necessary data from the request body and performs validation and data storage processing.

In your handler function, if you want to process the request asynchronously

It uses async and await, and try-catch syntax for error handling. If an error occurs, the appropriate status code and error message are returned as a response.

Here’s an example of a handler in Deno’s Oak. This allows you to implement any logic you want inside the handler function, such as handling requests or manipulating data.

Validation

Oak itself has no built-in validation functionality, but you can use Deno’s other libraries to do it for you.

import { Application, Router } from "https://deno.land/x/oak/mod.ts";
import * as yup from "https://deno.land/x/yup/mod.ts";

const router = new Router();
const app = new Application();

// Define user data validation schema
const userSchema = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required().positive().integer(),
  email: yup.string().required().email(),
});

// Define route and apply validation for POST method
router.post('/users', async (context) => {
  const { value } = await context.request.body();
  
  try {
    // run validation
    const validatedData = await userSchema.validate(value);

    // process user data
    const userData = { ...validatedData };

    // send response
    context.response.body = userData;
  } catch (error) {
    context.response.status = 400;
    context.response.body = error.errors;
  }
});

app.use(router.routes());

// start the server
app.listen({ port: 3000 });
console.log('Server started on port 3000');

In the above example, we are using a library called yup to validate the request. Defines validation rules for user data. Each field specifies a data type and validation rules.

The route definition for the POST method verifies that the request body data conforms to the validation rules. If there is a validation error, an error message will be returned as a response.

This allows you to use Oak to validate requests and generate flexible responses.

Oak Features

Asynchronous processing and event-driven architecture:

Oak also employs asynchronous processing and an event-driven architecture leveraging Deno’s EventEmitter. This allows requests to be processed in parallel at the same time. Compared to traditional synchronous web frameworks, Oak offers asynchronous processing flexibility and efficiency. With its event-driven architecture, Oak takes full advantage of the properties of the event loop to efficiently handle asynchronous processing of requests. This allows multiple requests to be processed simultaneously, resulting in faster response times and better scalability.

Resource Optimization with Middleware Support

Oak leverages middleware for handling HTTP requests and responses. This makes it easy to specify functions to run within the request and response lifecycle.

Additionally, Oak focuses on resource optimization. Detail optimizations such as dependency management and memory usage optimizations. This allows Oak applications to run efficiently and provide highly scalable performance.

Documentation is published on the official website, so let’s check how good it is.

High scalability

Oak leverages the concept of middleware and is easily customizable and extensible. By using a large number of middleware, it becomes easy to add functions and incorporate modules. Also, middleware can be added or removed without restarting the application.

Simple handling of requests and responses

Oak simplifies request and response handling

has become This allows developers to easily create control flow and error handling. All of this takes full advantage of Deno’s asynchronous processing and event-driven architecture.