Skip to main content

Kaksik

Middleware library for creating applications for Gemini protocol on top of Deno runtime, written in TypeScript.

Heavily inspired by oak and denoscuri.

Feature roadmap

  • Serve gemtext (out of the box, see TODO: Gemtext docs)
  • Serve static files at configured URLs (via middleware, see serveStatic)
  • Serve programmable resources at configured URLs (via middleware, see handleRoutes)
  • Serve redirect responses at configured URLs (via middleware, see handleRedirects)
  • Serve gone responses at configured URLs (via middleware)
  • Improve Response class
  • Document Gemtext usage
  • – ‘Good enough’ point –
  • Propose yours by filing an issue

Usage

Prerequisites

  1. Install Deno executable
  2. Obtain SSL certificates. You can generate self-signed ones using openssl command:
    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

Your first app

Create minimal application in app.ts:

import { Application } from 'https://deno.land/x/kaksik/mod.ts'

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(ctx => {
  ctx.response.body = '# Hello World!'
})

await app.start()

Then run it:

deno run --allow-net --allow-read app.ts

Other examples

See examples folder.

Available middleware

serveStatic

Serves static files from a directory to specified URL

import { Application, serveStatic } from 'https://deno.land/x/kaksik/mod.ts'

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(serveStatic('./log/', '/gemlog/'))
app.use(serveStatic('./public/'))

await app.start()

Beware of ordering of serveStatic middleware usages: more generic URLs should occur later that more specific, e.g., /path/subpath/ must be before /path/.

handleRoutes

Runs specified async function when request path matches configured route.

import {
  Application,
  handleRoutes,
  Route,
} from 'https://deno.land/x/kaksik/mod.ts'

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(handleRoutes(
  new Route('/test', async (ctx) => {
    ctx.response.body = '# Test page'
  }),
  new Route<{id?: string}>('/param/:id', async (ctx) => {
    ctx.response.body = '# Parametrized page\r\n' +
      'id = ' + ctx.pathParams.id
  }),
  new Route('/', async (ctx) => {
    ctx.response.body = '# HOME page\r\n' +
      '=> /test Test page served by other route\r\n' +
      '=> /param/7 Parametrized page, where id=7\r\n' +
      '=> /404 No routes matched'
  }),
))

app.use(async (ctx) => {
  ctx.response.body = '# No routes matched\r\n' +
    'Running fallback middleware'
})

await app.start()

handleRedirects

Sends either temporary or permanent redirect response when path matches configuration.

import {
  Application,
  handleRedirects,
  handleRoutes,
  Redirect,
  Route,
} from 'https://deno.land/x/kaksik/mod.ts'

const app = new Application({
  keyFile: '/path/to/key.pem',
  certFile: '/path/to/cert.pem',
})

app.use(handleRedirects(
  new Redirect('/short', '/long-very-long-url', true),
  new Redirect('/home', 'https://tymo.name'),
))

app.use(handleRoutes(
  new Route('/long-very-long-url', async (ctx) => {
    ctx.response.body = '# Redirect target page'
  }),
))

await app.start()

Trivia

“Kaksik” means “twin” in Estonian.