Published on

Using Absolute Twig File Paths with Fastify

Fastify is a Node.js web framework inspired by Express, offering an excellent developer experience thanks to its plugin-based architecture.

Having worked extensively with Express in the past, I now use Fastify exclusively for building APIs or web applications when JavaScript is the best fit for the project.

When developing a web application with server-side template rendering, I prefer Twig, my templating engine of choice. I’ve gained solid experience with Twig, particularly through its integration into Drupal and WordPress (via Timber).

Using Twig with Fastify

To use Twig with Fastify, you just need to create a plugin like this:

import path from 'path';

import FastifyPlugin from 'fastify-plugin';
import FastifyView from '@fastify/view';

import twig from 'twig';

// Adjust this to match your file structure
const viewsFolder = path.join(__dirname, '../views');

const plugin = FastifyPlugin(async (fastify, options) => {
  fastify.register(FastifyView, {
    engine: { twig },
    root: viewsFolder,
  });
});

export default plugin;

Add this plugin to Fastify using fastify.register, or via the @fastify/autoload module. Once set up, you can render Twig templates with reply.view(<chemin_vers_votre_template>).

The Issue with Relative Paths

If you’re like me and are used to working with Twig in environments where it’s natively integrated, you may notice a small inconvenience with this implementation: when including or extending templates, paths must be declared relative to the current file.

{# Works #}
{% extends '../../layouts/base.twig' %}

{# Doesn’t work #}
{% extends 'layouts/base.twig' %}

Adding a Base Path to the Twig Engine

To allow the use of absolute paths, it’s possible to override the twig function exported by the Twig module and add a base parameter pointing to the templates directory.

Here’s how you can integrate this change into your plugin:

import path from 'path';

import FastifyPlugin from 'fastify-plugin';
import FastifyView from '@fastify/view';

import twig from 'twig';

// Adjust this to match your file structure
const viewsFolder = path.join(__dirname, '../views');

// Override the twig function to include a base path
const originalTwig = twig.twig;
twig.twig = (params: twig.Parameters) => {
  return originalTwig({ ...params, base: viewsFolder });
};

const plugin = FastifyPlugin(async (fastify, options) => {
  fastify.register(FastifyView, {
    engine: { twig },
    root: viewsFolder,
  });
});

export default plugin;