Build and Dockerize a Blogging API With Deno, Oak, and MySQL
By: Farhan Hasin Chowdhury
Tags: Deno, JavaScript, Docker, Programming, Containers

Introduction to Oak

Oak is a middleware framework for Deno inspired by the popular Koa middleware framework. Before we begin working on the project, an understanding of a few concepts like middleware and routing is crucial.

Middleware

Oak middleware are functions that execute during the lifecycle of a request to the server. All middleware in Oak has access to a context object. To see a middleware in action, create a file app.ts somewhere in your computer and put following code in it:

The code can be run by executing following command in your terminal:

deno run --allow-net app.ts

Deno uses URLs for importing modules and third-party modules can be pulled in from https://deno.land/x , a hosting service for ES modules.

Once the Application class is imported from Oak, an app instance can be created. The app.use() function is used for registering middleware. The middelware function is passed as a parameter to the app.use() call. The context object is usually denoted by ctx and contains things like the request and response objects. If you want to learn about the context in more detail, follow this link.

A middleware can either end a request by returning a response or it can pass it to the next one using the next method. Middleware is processed as stack. A more complex example with middleware for logging incoming requests with response time can be created with following the code:

Two new middleware have been added for logging the incoming request and the time took to respond in the console.

The logger middleware just logs the value of X-Response-Time header along with the request method and URL and the timer middleware sets the value of X-Response-Time header used in our previous middleware.

All the three midleware in this program will be stacked on top of one another and executed in the order we register them in the code. At first the logger will run, then the timer middleware and at last the hello world middleware. You can test out the code again by restarting the server.

Middleware can be exported and imported as ES modules. Create a directory called middleware in the root of your project and create two files named logger.ts and timer.ts in there. Now extract the code for logger middleware and put that inside the logger.ts file:

export default async (ctx: any, next: any) => {

await next();

const rt = ctx.response.headers.get("X-Response-Time");

console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);

}

Now for the timer.ts :

export default async (ctx: any, next: any) => {

const start = Date.now();

await next();

const ms = Date.now() - start;

ctx.response.headers.set("X-Response-Time", `${ms}ms`);

}

Now, import these two exported functions inside app.ts and register using the app.use() function:

That’s better. This directory will be now used as our project root. As we go forward more complex middleware will be added.

Routing

In Oak, the Router class can be used for producing middleware to enable routing based on the path-name of the request. So far the application responds with “hello world” no matter what endpoint we hit — that’s not what we want. So, update the code for hello world middleware inside app.ts to look like this:

To use the Router class, an instance of it has to be created. Routes with GET , POST , PUT , PATCH , DELETE methods can be created by calling the corresponding function on the Router instance. You can learn more about this class by following this link.

Each route registration takes the path-name as a string and a function as middleware. Just like the app.use() call, all route middleware has access to the context.

The status code for the response can be set using ctx.response.status property. A status is a simple number, the Status class provides properties containing status codes for various situations; so, Status.OK is 200, Status.NotFound is 404 — you get the idea.

Type and contents of the response can be set using ctx.response.type and ctx.response.body properties.

Routes can be registered in the app instance using app.use() call passing router.routes() as a parameter, where router is the instance of Router class.

I’m using JSend — a specification for a simple, no-frills, JSON based format for application-level communication but you’re free to use whatever you like.