Files
fornix/DEVELOP_README.md
2025-12-19 16:16:57 +01:00

5.1 KiB

Drupal development

Drupal is the headless CMS. Editors manage content in Drupal, and the F# app fetches it via JSON:API. Everything Drupal needs to run is versioned under drupal/ so environments can be reproduced from Git.

Docs

Repo file structure

  • drupal/web Drupal docroot (what the container serves)

  • drupal/config Exported config sync dir (content types, fields, views, roles, enabled modules, workflows, permissions)

  • drupal/composer.json + drupal/composer.lock PHP dependencies (Drupal core, contrib modules, Drush)

  • drupal/web/modules/custom/oceanbox Custom module for the editorial workflow

Drupal admin UI

Typical places in the admin UI:

  • Content list: /admin/content
  • Structure: /admin/structure
    • Used for creating content types, fields, views, and other elements of site structure.
  • Configuration: /admin/config
  • Status report: /admin/reports/status
    • Any logging, errors, or issues with the Drupal installation will show up here.
  • Config sync UI: /admin/config/development/configuration
    • Although config is managed via Drush commands, this UI provides a way to review and sync configuration changes visually.

JSON:API usually lives under /jsonapi/... (exact routes depend on enabled modules and entity types). We also use Decoupled Router for path alias resolution.

Custom module: oceanbox

Location: drupal/web/modules/custom/oceanbox

What it does:

  • Adds Save and view draft on node edit pages.
  • Redirects after saving to the front end URL for that node.
  • Adds a View draft action in the Content list when the latest revision is still a draft.
  • Removes the core Preview action.
  • Rewrites the Content list title to link to the published front end page.

Front end URL source:

  • Uses oceanbox.settings: frontend_base_url (supports per-language configuration).
  • Falls back to Drupal paths if the setting is missing.

Patches

We use some patches to make sure that JSON:API is structured in a sensible way for our front end. Patches are applied via Composer when dependencies are installed. In case they're not installed you can do: composer patches-repatch, and in case of adding a new patch you can use: composer patches-relock. If in the future we upgrade Drupal or any module that has a patch applied, make sure to check if the patch is still needed or if it has been merged upstream.

Configuration workflow (staging first)

Drupal config is YAML on disk. Drupal content is in the database. We keep config in Git so every environment behaves the same.

If you change config in staging, export it back into Git

If someone makes changes in the staging admin UI (fields, views, permissions, workflows, enabled modules), treat it as incomplete until the config is exported and committed.

Adding or updating PHP modules

  • Update dependencies in Git under drupal/:

    • Update drupal/composer.json and drupal/composer.lock (wherever Composer is run for the build pipeline)
  • After deploying the new image to staging:

    • Enable the module (if needed) and export config:
      drush en <module> -y
      drush cex -y
      
    • Copy exported config back into drupal/config and commit it

Safe defaults

  • Any UI tweak that changes Drupal behavior is almost always config. Export it and commit it.
  • Avoid drift in staging by treating drupal/config as the source of truth and regularly reconciling via drush cex and drush cim.
  • When something looks wrong after config or module changes use drush cr.

F# development

The F# app is the front end that fetches content from Drupal via JSON:API and renders it for users. It lives under src/. We use three different projects:

  • Server: The main web app
  • Shared: Shared code between server and client
  • Client: Fable code that runs in the browser

Running the F# app locally

Start the F# app using just run. It will connect to the staging Drupal instance by default. You can change the Drupal URL and Frontend base URL using default.nix.

Server

The server project handles server-side rendering, routing, and API requests, and serves the static files for the client-side application. We use different methods to detect the language, and retrieving the right content from Drupal.

Shared

The shared project contains code that is used by both the server and client projects, such as types, utility functions, and components rendered.

Client

The client project contains Fable code that runs in the browser, handling interactivity and client-side. However, most of the rendering is done on the server side for better performance and SEO, and this project also includes the shared code so most of the component/pages/layout changes should be done in the shared project.