How to create middleware in Deno's Oak

overview

Oak is a framework with the ability to perform processing between requests and responses using middleware. Middleware provides access to request objects, response objects, and the following middleware functions: Middleware is used within Oak’s route handlers.

Here I will show you how to create middleware with Oak using the following versions:

  • Oak v9.0.0
  • Deno v1.15.3

In addition, all the code created this time is posted on the following GitHub repository.

https://github.com/wiblok/Oak.js

How to define middleware

In Oak.js the main way to use middleware looks like this:

  1. Application level middleware Middleware that applies to the entire application. Define using app.use().
import { Application } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

app. use((context, next) => {
  console.log('This is an application-level middleware');
  next();
});
  1. Router level middleware Middleware applied to a specific route or route group. Define using router.use().
import { Application, Router } from "https://deno.land/x/oak/mod.ts";

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

router.use((context, next) => {
  console.log('This is a router-level middleware');
  next();
});

app.use(router.routes());
app.use(router.allowedMethods());

These middlewares help perform functions at specific points as individual requests pass through your application. Each middleware function either completes the request-response cycle or passes the request and response objects to the next middleware function in the stack.

How to create application level middleware

The example below creates an application-level middleware named myMiddleware.

application.ts

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

const app = new Application();

const myMiddleware = async (context, next) => {
  // middleware processing
  console.log('run middleware');
  await next();
}

app. use(myMiddleware);

app. use((context) => {
  context.response.body = { message: 'Hello, World!' };
});

await app.listen({ port: 8000 });

This code is for creating a web server with Deno using the Oak framework. Below is a bullet point description of the code.

  • import { Application } from "https://deno.land/x/oak/mod.ts";
    • Import the Oak framework with the name Application.
  • const app = new Application();
    • Call new Application() to create a new Oak application and assign it to the app variable.
  • const myMiddleware = async (context, next) => { ... }
    • Define a function named myMiddleware.
    • This function is called middleware and is used by Oak to process requests.
  • console.log('running middleware');
    • As part of the processing within the middleware function, output the message “middleware execution” to the console.
  • app.use(myMiddleware);
    • Apply the created middleware function (myMiddleware) to your Oak application using the app.use method.
    • This will force all requests to go through this middleware function.
  • app.use((context) => { ... });
    • Define a route handler for GET requests using the app.use method.
    • In the processing inside the arrow function,

I am sending a JSON formatted response using the context.response.body property.

  • await app.listen({ port: 8000 });
    • Start the application on port 8000 using the app.listen method.

You can send a request using the command below.

curl -X GET http://localhost:8000

How to create router level middleware

The example below creates a router-level middleware named myMiddleware.

routeMiddleware.ts

const myMiddleware = async (context, next) => {
  // middleware processing
  console.log('run middleware');
  await next();
}

export default myMiddleware;

routeMain.ts

import { Application } from "https://deno.land/x/oak/mod.ts";
import myMiddleware from './routeMiddleware.ts';

const app = new Application();

app. use(myMiddleware);

app. use((context) => {
  context.response.body = { message: 'Hello, World!' };
});

await app.listen({ port: 3000 });
console.log('Server is running on port 3000');

When I run this code, it runs the middleware for every request and prints ‘running middleware’ to the console.

curl -X GET http://localhost:3000

How to use built-in middleware

The Oak framework has built-in middlewares, which provide specific functionality. For example, you can serve static files with code like this:

builtin.ts

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

const app = new Application();

// Serve static files using send middleware
app. use(async (context) => {
  await send(context, context.request.url.pathname, {
    root: `${Deno.cwd()}/public`,
  });
});

await app.listen({ port: 3000 });
console.log('Server is running on port 3000');

This code serves static files in the public directory. For example, if you access http://localhost:3000/sample.txt in your browser, you will see public/sample.txt.

curl -X GET http://localhost:3000/sample.txt

How to use 3rd party middleware

Deno can take advantage of third-party middleware available from deno.land/x. For example, you can use middleware for log output called oak_middleware_logger with the following code.

deno install oak_middleware_logger

Now write the code to incorporate this package into your application.

thirdparty.ts

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

ak_middleware_logger/mod.ts";

const app = new Application();

// output log using logger middleware
app.use(logger.logger);
app.use(logger.responseTime);

app. use((context) => {
  context.response.body = { message: 'Hello, World!' };
});

await app.listen({ port: 3000 });
console.log('Server is running on port 3000');

This code runs the oak_middleware_logger middleware for every request, which prints the details of the HTTP request to the console.

curl -X GET http://localhost:3000

How to create error handling middleware

Error handling middleware handles errors passed to it by other middleware. Below is an example of creating an error handling middleware.

errorhandling.ts

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

const app = new Application();

app. use(async (context, next) => {
  try {
    await next();
  } catch (err) {
    console. error(err. message);
    context.response.status = 500;
    context.response.body = 'Something went wrong';
  }
});

app. use((context) => {
  throw new Error('Something went wrong');
});

await app.listen({ port: 3000 });
console.log('Server is running on port 3000');

This code throws an error after the error handling middleware has run. This error is caught by the error handling middleware and an error message is printed to the console. Also, an error message is returned to the client with a 500 error (Internal Server Error).

curl -X GET http://localhost:3000