Skip to content

Koa compatible router

As mentioned in the targets, Compas supports generating a Koa compatible router.

js
import { Generator } from "@compas/code-gen";

const generator = new Generator();

// Build your own structure or import an existing one

generator.generate({
  targetLanguage: "js",
  outputDirectory: "./src/generated",
  generators: {
    router: {
      target: {
        library: "koa",
      },
      exposeApiStructure: true,
    },
  },
});

Setup

js
import { getApp, createBodyParsers } from "@compas/server";
import { router } from "./src/generated/common/router.js";

// Includes logging, error handling, CORS handling and some more, returning a Koa
// application.
const app = getApp();

// Add your own middleware and custom route handlers
app.use((ctx, next) => {
  // ...
  return next();
});

// And finally use the generated router middleware
// We pass in a compatible body parser from @compas/server as well.
app.use(router(createBodyParsers()));

app.listen(3000);
js
import Koa from "koa";
import { router } from "./src/generated/common/router.js";

const app = new Koa();

// Add your own middleware and custom route handlers
app.use((ctx, next) => {
  console.log(ctx.method, ctx.path);
  return next();
});

// And finally use the generated router middleware
app.use(
  router({
    bodyParser: async (ctx) => {
      // use a library to parse the request body
    },
  }),
);

app.listen(3000);

All your routes are now be accessible but return a 405 Not Implemented HTTP status. Let's see how to add route implementations.

Implementing controllers

Implementing the controllers is done by overwriting their generated implementations.

js
// Note how we import the handlers or controller from the generated files
import { userHandlers } from "./src/generated/user/controller.js";

userHandlers.single = async (ctx) => {
  // ctx is a Koa context.
  // Set the response, like you'd normally do in Koa.
  ctx.body = {
    email: "my@email.com",
  };
};

import { imaginaryHandlers } from "./src/generated/imaginary/controller.js";

// Validators are integrated in to the generated router.
imaginaryHandlers.route = async (ctx) => {
  // `R.get("/:userId", "route").params({ userId: T.uuid() })`
  // `GET /e4dbc7dd-adfb-4fce-83f6-f5dcaf68c30c`
  // results in `ctx.validatedParams`. The router will automatically return a `400 Bad Request` if the `userId` is not a valid uuid.
  ctx.validatedParams.userId; // -> uuid

  // `R.get("/", "route").query({ offset: T.number().default(0) })`
  // `GET /?offset=30
  ctx.validatedQuery.offset; // -> number (30)

  // `R.post("/", "route").body({ tags: [T.string()] })`
  // Request body is validated as well
  ctx.validatedBody.tags; // -> string[]

  // `R.get("/", "route").response({ foo: "bar" })`
  // Responses are validated as well. This ensures that you as the API creator adhere to the structure (or contract).
  ctx.body = {}; // throws a `500 Internal Server Error`, response did not pass validators
  ctx.body = { foo: "bar" }; // returns a `200 OK`.
};

Integrate with other Compas features

Compas provides a few features to make working with Koa and Compas a more integrated experience. Take a look at for example session handling or file handling.