MCPcopy Index your code
hub / github.com/oakserver/oak

github.com/oakserver/oak @v17.2.0 sqlite

repository ↗ · DeepWiki ↗ · release v17.2.0 ↗
470 symbols 1,092 edges 61 files 68 documented · 14%
README

oak

jsr.io/@oak/oak jsr.io/@oak/oak score deno.land/x/oak npm Version

oak ci codecov

A middleware framework for Deno's native HTTP server, Deno Deploy, Node.js 16.5 and later, Cloudflare Workers and Bun. It also includes a middleware router.

This middleware framework is inspired by Koa and middleware router inspired by @koa/router.

This README focuses on the mechanics of the oak APIs and is intended for those who are familiar with JavaScript middleware frameworks like Express and Koa as well as a decent understanding of Deno. If you aren't familiar with these, please check out documentation on oakserver.github.io/oak.

Also, check out our FAQs and the awesome-oak site of community resources.

[!NOTE] The examples in this README pull from main and are designed for Deno CLI or Deno Deploy, which may not make sense to do when you are looking to actually deploy a workload. You would want to "pin" to a particular version which is compatible with the version of Deno you are using and has a fixed set of APIs you would expect. https://deno.land/x/ supports using git tags in the URL to direct you at a particular version. So to use version 13.0.0 of oak, you would want to import https://deno.land/x/oak@v13.0.0/mod.ts.

Usage

Deno CLI and Deno Deploy

oak is available on both deno.land/x and JSR. To use from deno.land/x, import into a module:

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

To use from JSR, add it to your project:

deno add jsr:@oak/oak

Then import into a module:

import { Application } from "@oak/oak";

Node.js

oak is available for Node.js on both npm and JSR. To use from npm, install the package:

npm i @oakserver/oak

And then import into a module:

import { Application } from "@oakserver/oak";

To use from JSR, install the package:

npx jsr i @oak/oak

And then import into a module:

import { Application } from "@oak/oak/application";

[!NOTE] Send, websocket upgrades and serving over TLS/HTTPS are not currently supported.

In addition the Cloudflare Worker environment and execution context are not currently exposed to middleware.

Cloudflare Workers

oak is available for Cloudflare Workers on JSR. To use add the package to your Cloudflare Worker project:

npx jsr add @oak/oak

And then import into a module:

import { Application } from "@oak/oak/application";

Unlike other runtimes, the oak application doesn't listen for incoming requests, instead it handles worker fetch requests. A minimal example server would be:

import { Application } from "@oak/oak/application";

const app = new Application();

app.use((ctx) => {
  ctx.response.body = "Hello CFW!";
});

export default { fetch: app.fetch };

[!NOTE] Send and websocket upgrades are not currently supported.

Bun

oak is available for Bun on JSR. To use install the package:

bunx jsr i @oak/oak

And then import into a module:

import { Application } from "@oak/oak/application";

[!NOTE] Send and websocket upgrades are not currently supported.

Application, middleware, and context

The Application class coordinates managing the HTTP server, running middleware, and handling errors that occur when processing requests. Two of the methods are generally used: .use() and .listen(). Middleware is added via the .use() method and the .listen() method will start the server and start processing requests with the registered middleware.

A basic usage, responding to every request with Hello World!:

import { Application } from "@oak/oak/application";

const app = new Application();

app.use((ctx) => {
  ctx.response.body = "Hello World!";
});

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

You would then run this script in Deno like:

> deno run --allow-net helloWorld.ts

For more information on running code under Deno, or information on how to install the Deno CLI, check out the Deno manual.

The middleware is processed as a stack, where each middleware function can control the flow of the response. When the middleware is called, it is passed a context and reference to the "next" method in the stack.

A more complex example:

import { Application } from "@oak/oak/application";

const app = new Application();

// Logger
app.use(async (ctx, next) => {
  await next();
  const rt = ctx.response.headers.get("X-Response-Time");
  console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});

// Timing
app.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const ms = Date.now() - start;
  ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});

// Hello World!
app.use((ctx) => {
  ctx.response.body = "Hello World!";
});

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

To provide an HTTPS server, then the app.listen() options need to include the options .secure option set to true and supply a .certFile and a .keyFile options as well.

.handle() method

The .handle() method is used to process requests and receive responses without having the application manage the server aspect. This though is advanced usage and most users will want to use .listen().

The .handle() method accepts up to three arguments. The first being a Request argument, and the second being a Deno.Conn argument. The third optional argument is a flag to indicate if the request was "secure" in the sense it originated from a TLS connection to the remote client. The method resolved with a Response object or undefined if the ctx.respond === true.

An example:

import { Application } from "@oak/oak/application";

const app = new Application();

app.use((ctx) => {
  ctx.response.body = "Hello World!";
});

Deno.serve(async (request, info) => {
  const res = await app.handle(request, info.remoteAddr);
  return res ?? Response.error();
});

An instance of application has some properties as well:

  • contextState - Determines the method used to create state for a new context. A value of "clone" will set the state as a clone of the app state. A value of "prototype" means the app's state will be used as the prototype of the context's state. A value of "alias" means that the application's state and the context's state will be a reference to the same object. A value of "empty" will initialize the context's state with an empty object.

  • .jsonBodyReplacer - An optional replacer function which will be applied to JSON bodies when forming a response.

  • .jsonBodyReviver - An optional reviver function which will be applied when reading JSON bodies in a request.

  • .keys

Keys to be used when signing and verifying cookies. The value can be set to an array of keys, and instance of KeyStack, or an object which provides the same interface as KeyStack (e.g. an instance of keygrip). If just the keys are passed, oak will manage the keys via KeyStack which allows easy key rotation without requiring re-signing of data values.

  • .proxy

This defaults to false, but can be set via the Application constructor options. This is intended to indicate the application is behind a proxy and will use X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-For when processing the request, which should provide more accurate information about the request.

  • .state

A record of application state, which can be strongly typed by specifying a generic argument when constructing an Application(), or inferred by passing a state object (e.g. Application({ state })).

Context

The context passed to middleware has several properties:

  • .app

A reference to the Application that is invoking this middleware.

  • .cookies

The Cookies instance for this context which allows you to read and set cookies.

  • .request

The Request object which contains details about the request.

  • .respond

Determines if when middleware finishes processing, the application should send the .response to the client. If true the response will be sent, and if false the response will not be sent. The default is true but certain methods, like .upgrade() and .sendEvents() will set this to false.

  • .response

The Response object which will be used to form the response sent back to the requestor.

  • .socket

This will be undefined if the connection has not been upgraded to a web socket. If the connection has been upgraded, the .socket interface will be set.

  • .state

A record of application state, which can be strongly typed by specifying a generic argument when constructing an Application(), or inferred by passing a state object (e.g. Application({ state })).

The context passed to middleware has some methods:

  • .assert()

Makes an assertion, which if not true, throws an HTTPError, which subclass is identified by the second argument, with the message being the third argument.

  • .send()

Stream a file to the requesting client. See Static content below for more information.

  • .sendEvents()

Convert the current connection into a server-sent event response and return a ServerSentEventTarget where messages and events can be streamed to the client. This will set .respond to false.

  • .throw()

Throws an HTTPError, which subclass is identified by the first argument, with the message being passed as the second.

  • .upgrade()

Attempt to upgrade the connection to a web socket connection, and return a WebSocket interface. Previous version of oak, this would be a Promise resolving with a std/ws web socket.

Unlike other middleware frameworks, context does not have a significant amount of aliases. The information about the request is only located in .request and the information about the response is only located in .response.

Cookies

The context.cookies allows access to the values of cookies in the request, and allows cookies to be set in the response. It automatically secures cookies if the .keys property is set on the application. Because .cookies uses the web crypto APIs to sign and validate cookies, and those APIs work in an asynchronous way, the cookie APIs work in an asynchronous way. It has several methods:

  • .get(key: string, options?: CookieGetOptions): Promise<string | undefined>

Attempts to retrieve the cookie out of the request and returns the value of the cookie based on the key. If the applications .keys is set, then the cookie will be verified against a signed version of the cookie. If the cookie is valid, the promise will resolve with the value. If it is invalid, the cookie signature will be set to deleted on the response. If the cookie was not signed by the current key, it will be resigned and added to the response.

  • .set(key: string, value: string, options?: CookieSetDeleteOptions): Promise<void>

Will set a cookie in the response based on the provided key, value and any options. If the applications .keys is set, then the cookie will be signed and the signature added to the response. As the keys are signed asynchronously, awaiting the .set() method is advised.

Request

The context.request contains information about the request. It contains several properties:

  • .body

An object which provides access to the body of the request. See below for details about the request body API.

  • .hasBody

Set to true if the request might have a body, or false if it does not. It does not validate if the body is supported by the built in body parser though.

[!WARNING] This is an unreliable API. In HTTP/2 in many situations you cannot determine if a request has a body or not unless you attempt to read the body, due to the streaming nature of HTTP/2. As of Deno 1.16.1, for HTTP/1.1, Deno also reflects that behavior. The only reliable way to determine if a request has a body or not is to attempt to read the body.

It is best to determine if a body might be meaningful to you with a given method, and then attempt to read and process the body if it is meaningful in a given context. For example GET and HEAD should never have a body, but methods like DELETE and OPTIONS might have a body and should be have their body conditionally processed if it is meaningful to your application.

  • .headers

The headers for the request, an instance of Headers.

  • .method

A string that represents the HTTP method for the request.

  • .originalRequest

DEPRECATED this will be removed in a future release of oak.

The "raw" NativeServer request, which is an abstraction over the DOM Request object. `.originalRequest.r

Extension points exported contracts — how you extend this code

MiddlewareObject (Interface)
(no doc) [7 implementers]
middleware.ts
ServerRequest (Interface)
(no doc) [4 implementers]
types.ts
Bun (Interface)
(no doc)
http_server_bun.ts
NativeRequestInfo (Interface)
(no doc)
http_server_native_request.ts
ContextOptions (Interface)
(no doc)
context.ts
OakRequestOptions (Interface)
(no doc)
request.ts
Matches (Interface)
(no doc)
router.ts
MockContextOptions (Interface)
(no doc)
testing.ts

Core symbols most depended-on inside this repo

get
called by 160
router.ts
assert
called by 126
testing.ts
use
called by 57
router.ts
toDomResponse
called by 56
response.ts
routes
called by 53
router.ts
use
called by 47
application.ts
listen
called by 33
application.ts
text
called by 32
body.ts

Shape

Method 232
Function 133
Interface 53
Class 52

Languages

TypeScript100%

Modules by API surface

router.ts58 symbols
application.ts45 symbols
http_server_bun.ts29 symbols
middleware/serve.ts24 symbols
request.ts23 symbols
types.ts22 symbols
application.test.ts20 symbols
http_server_native_request.ts19 symbols
response.ts18 symbols
body.ts17 symbols
http_server_node.ts16 symbols
context.ts15 symbols

For agents

$ claude mcp add oak \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact