Fresh logo

Initializing the server

Let’s pretend you’ve just initialized a new Fresh project. You want to do some complicated setup that runs once, before the server is started. This is, fortunately, quite easy. Here’s how:

main.ts
 import { start } from "$fresh/server.ts";
 import manifest from "./fresh.gen.ts";
+import { Context } from "./routes/_middleware.ts";

+await Context.init();
 await start(manifest);

So your full main.ts should look like this:

main.ts
/// <reference no-default-lib="true" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
/// <reference lib="deno.ns" />

import "$std/dotenv/load.ts";

import { start } from "$fresh/server.ts";
import manifest from "./fresh.gen.ts";
import { Context } from "./routes/_middleware.ts";

await Context.init();
await start(manifest);

But what’s going on in this new _middleware.ts we’ve created?

routes/_middleware.ts
import { MiddlewareHandlerContext } from "$fresh/server.ts";

export interface State {
  context: Context;
}

export class Context {
  private static context: Context;
  private complicatedStartupValue: number;

  public constructor() {
    // presumably this involves connecting to a
    // database or doing some heavy computation
    this.complicatedStartupValue = 42;
  }

  public static async init() {
    Context.context = new Context();
  }

  public static instance() {
    if (this.context) return this.context;
    else throw new Error("Context is not initialized!");
  }
}

export async function handler(
  req: Request,
  ctx: MiddlewareHandlerContext<State>,
) {
  ctx.state.context = Context.instance();
  const resp = await ctx.next();
  return resp;
}

So now in this handler (or any other handler functions you create) you can have access to the complicated initialization step by calling Context.instance().