In practice, turning Directus into the backbone of a production backend takes a fair amount of setup before a project becomes productive. is the projen template we built to automate that plumbing around directus. By default it targets is a GPL fork of Directus v9 we maintain on GitHub. It does what Directus 9 did: an open-source (GPL-3 license) CMS that, with the right discipline, can stand in for a full custom backend (API, admin UI, permissions, schema), with business logic added as extensions (hooks, endpoints, operations, custom UI panels).
Bootstrap a new project
npx projen new --from @wbce/projen-d9
This creates a .projenrc.js and synthesizes the project.
What you get
The user-facing surface of the template is .projenrc.js:
import { D9Project } from '@wbce/projen-d9';
import { D9ExtensionType } from '@wbce/projen-d9-extension';
const project = new D9Project({
name: 'my-backend',
defaultReleaseBranch: 'main',
});
project.addExtension('audit-log', [D9ExtensionType.HOOK]);
project.synth();
npx projen synthesizes everything else (Dockerfile, docker-compose, extension folder, build pipeline, GitHub workflows). (set
githubConfig: false to disable)
A standardized compose stack
With a single CLI command, you can spin up a fully working local Directus environment (database, cache, and API) ready for development. It mirrors a production setup, including caching and geospatial capabilities.
The compose file is built with projen's DockerCompose construct:
database:postgis/postgis:13-masterin order to enable geospatial functionality
cache:redis:6
directus: built from the localDockerfile.EXTENSIONS_AUTO_RELOAD=trueis set, so re-runningnpx projen build-extensionsis enough to pick up edits with no container restart.
Bind mounts:
./uploads -> /app/uploads
./extensions -> /app/extensions(the extension tree thatbuild-extensionswrites into)
./.env.local -> /app/.env.local(so secrets stay out of the image and git versioning)
Extensions as a pnpm workspace
addExtension(name, types, options?)adds an ExtensionFolder to the parent project. Each addExtension creates a D9ExtensionProject with three quirks:
- Its
package.jsongets thedirectus:extensionfield correctly configured. - Its build task is rewritten to compile and then deposit the output in the right subfolder of
extensions/
- If any of the types are UI extensions (
INTERFACE,DISPLAY,LAYOUT,MODULE,PANEL),vueis added as a devDep automatically.
Because every extension is its own package in a real pnpm workspace, we can share packages between extensions :
// A shared library, no extension types
project.addExtension('shared', []);
// Other extensions depend on it via workspace:
const myHook = project.addExtension('audit-log', [D9ExtensionType.HOOK]);
myHook.addDeps('shared@workspace:');
And
Links
- npm package: (GPL-3.0)
- d9: (GPL-3.0)
- projen: projen.io
Issues and PRs welcome.
SOCIAL SHARE CARD GENERATOR