Fresh logo

Server configuration

In this page we discuss how the server can be configured during startup.

The signature of the primary method looks like this:

main.ts
export async function start(manifest: Manifest, config: FreshConfig = {});

Configuration

Manifest comes from fresh.gen.ts, so nothing to do there. config is where things get interesting. FreshConfig looks like this:

export interface FreshConfig {
  build?: {
    /**
     * The directory to write generated files to when `dev.ts build` is run.
     * This can be an absolute path, a file URL or a relative path.
     */
    outDir?: string;
    /**
     * This sets the target environment for the generated code. Newer
     * language constructs will be transformed to match the specified
     * support range. See https://esbuild.github.io/api/#target
     * @default {"es2022"}
     */
    target?: string | string[];
  };
  render?: RenderFunction;
  plugins?: Plugin[];
  staticDir?: string;
  router?: RouterOptions;
  server?: Partial<Deno.ServeTlsOptions>;
}

And for completeness here are the remaining two types:

export type RenderFunction = (
  ctx: RenderContext,
  render: InnerRenderFunction,
) => void | Promise<void>;

export interface RouterOptions {
  /**
   *  Controls whether Fresh will append a trailing slash to the URL.
   *  @default {false}
   */
  trailingSlash?: boolean;
  /**
   *  Configures the pattern of files to ignore in islands and routes.
   *
   *  By default Fresh will ignore test files,
   *  for example files with a `.test.ts` or a `_test.ts` suffix.
   *
   *  @default {/(?:[^/]*_|[^/]*\.|)test\.(?:ts|tsx|mts|js|mjs|jsx|)\/*$/}
   */
  ignoreFilePattern?: RegExp;
  /**
   * Serve fresh from a base path instead of from the root.
   *   "/foo/bar" -> http://localhost:8000/foo/bar
   * @default {undefined}
   */
  basePath?: string;
}

Build

outDir

As the comment suggests, this can be used to configure where generated files are written:

await dev(import.meta.url, "./main.ts", {
  build: {
    outDir: Deno.env.get("FRESH_TEST_OUTDIR") ?? undefined,
  },
});

target

This should be a valid ES Build target.

await dev(import.meta.url, "./main.ts", {
  build: {
    target: "es2015",
  },
});

Plugins

See the docs on this topic for more detail. But as a quick example, you can do something like this to load plugins:

main.ts
await start(manifest, { plugins: [twindPlugin(twindConfig)] });

StaticDir

This allows you to specify the location where your site’s static assets are stored. Here’s an example:

main.ts
await start(manifest, { staticDir: "./custom_static" });

Render

This is by far the most complicated option currently available. It allows you to configure how your components get rendered.

RouterOptions

TrailingSlash

By default Fresh uses URLs like https://www.example.com/about. If you’d like, you can configure this to https://www.example.com/about/ by using the trailingSlash setting.

main.ts
await start(manifest, { router: { trailingSlash: true } });

ignoreFilePattern

By default Fresh ignores test files which are co-located next routes and islands. If you want, you can change the pattern Fresh uses ignore these files

basePath

This setting allows you to serve a Fresh app from sub-path of a domain. A value of /foo/bar would serve the app from http://localhost:8000/foo/bar instead of http://localhost:8000/ for example.

The basePath will be automatically applied to absolute links in your app. For example, when the basePath is /foo/bar, linking to /about will automatically become /foo/bar/about.

<a href="/about">About</a>;

Rendered HTML:

<a href="/foo/bar/about">About</a>

The basePath is also applied to the src and srcset attribute of <img>-tags, the href attribute of <link> and the src attribute of <script> tags.

Server

Now that Deno has stabilized Deno.serve and Fresh has switched to using this API, all server configuration options are embedded in server inside the FreshConfig. The fully expanded set of parameters looks like this:

server: {
  /** Server private key in PEM format */
  cert: string;

  /** Cert chain in PEM format */
  key: string;

  /** The port to listen on.
   *
   * @default {8000} */
  port?: number;

  /** A literal IP address or host name that can be resolved to an IP address.
   *
   * __Note about `0.0.0.0`__ While listening `0.0.0.0` works on all platforms,
   * the browsers on Windows don't work with the address `0.0.0.0`.
   * You should show the message like `server running on localhost:8080` instead of
   * `server running on 0.0.0.0:8080` if your program supports Windows.
   *
   * @default {"0.0.0.0"} */
  hostname?: string;

  /** An {@linkcode AbortSignal} to close the server and all connections. */
  signal?: AbortSignal;

  /** Sets `SO_REUSEPORT` on POSIX systems. */
  reusePort?: boolean;

  /** The handler to invoke when route handlers throw an error. */
  onError?: (error: unknown) => Response | Promise<Response>;

  /** The callback which is called when the server starts listening. */
  onListen?: (params: { hostname: string; port: number }) => void;
}

Use these to configure your server as you see fit.