npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

gatsby-source-directus-tegrus

v0.2.6

Published

A Gatsby source plugin to pull content from a Directus CMS backend.

Readme

Gatsby Source Directus

:warning: This project is under heavy development, so breaking changes may occur on our road to a stable v1.0.0. Any bug report will be highly appreciated.

A Gatsby source plugin to pull content from a Directus CMS backend.

Inspired by the gatsby-source-directus7 plugin by Joonas Palosuo

Features

  • Exposes all custom content types & associated records created in Directus as distinct nodes in the Gatsby GraphQL layer
  • Mirrors O2M, M2O, M2M, and "File" relations between content types in Directus in the Graph QL layer
  • Downloads files hosted in Directus for usage with other Gatsby transformer plugins

Installation

Installing the plugin is no different than installing other Gatsby source plugins.

  1. Create a gatsby project. For help, see the Gatsby quick start guide here
  2. Install the plugin using npm install --save @directus/gatsby-source-directus
  3. Edit your gatsby-config.js. See below for details.

Configuration

Options

Find details regarding the options object schema below.

| Field | Type | Default | Note | | ------------------ | ------------------------------------------------------------ | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | url | string | void | Required - The base url for the project's Directus API. | | project | string | void | Required - The target projects name in Directus. | | auth | { email: string; password: string; } \| { token: string; } | void | Either the login credentials for the user to authenticate the Directus API with, OR a token used to authenticate with the Directus API. If both are provided, the token is preferred. If neither are provided, the public API is used. | | targetStatuses | string[] \| void | ["published", "__NONE__"] | A set of allowed statuses records must match to be included in the mesh. A value of null or undefined includes content of any status. The string "__NONE__" can be used to allow records with no status defined. | | allowCollections | string[] \| void | void | A set of collection names to allow. Only collections with names that appear in the set will be included. void includes all collections. | | blockCollections | string[] \| void | void | A set of collection names to block. Only collections with names that don't appear in the set will be included. void blocks no collections. | | typePrefix | string | "Directus" | The prefix to use for the node types exposed in the GraphQL layer. | | includeJunctions | boolean | false | Allows inclusion of the junction tables that manage M2M relations in the GraphQL layer. | | downloadFiles | boolean | true | Indicates if files should be downloaded to disk. Enables images to be used with other transform plugins. Setting to false could be useful if the project has many files. | | customRecordFilter | ((record: any, collection: string) => boolean) \| void | void | A function executed for each record, returning whether the record should be included in the content mesh. Note: If provided, this will override any targetStatuses value. |

Directus Config

Setting up a separate user in Directus for usage with this plugin is recommended. Make sure you grant read privileges to the user on all tables, including system tables. See more in the Directus docs.

Example Configurations

Basic configuration including only published or statusless items.

// gatsby-config.js

module.exports = {
  // ...
  plugins: [
    {
      // ...
      resolve: '@directus/gatsby-source-directus',
      options: {
        url: 'https://directus.example.com',
        project: '_',
        auth: {
          email: '[email protected]',
          password: 'example',
        },
        targetStatuses: ['published', '__NONE__'],
      },
      // ...
    },
  ],
  // ...
};

Custom configuration including published, draft, or statusless items. Blocks the super_secret_stuff collection, and will not download files for processing by gatsby-image.

// gatsby-config.js

module.exports = {
  // ...
  plugins: [
    {
      // ...
      resolve: '@directus/gatsby-source-directus',
      options: {
        url: 'https://directus.example.com',
        project: '_',
        auth: {
          email: '[email protected]',
          password: 'example',
        },
        targetStatuses: ['published', 'draft', '__NONE__'],
        blockCollections: ['super_secret_stuff'],
        downloadFiles: false,
      },
      // ...
    },
  ],
  // ...
};

Usage Examples

Sample gatsby-node.js

const path = require('path');

module.exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  // Sample query for all blog posts.
  const response = await graphql(`
    query GatsbyBlogPostsQuery {
      allDirectusBlogPost {
        edges {
          node {
            directusId
            title
            author
            body
            preview
            created
            slug
          }
        }
      }
    }
  `);

  // Destructure response to get post data
  const {
    data: {
      allDirectusBlogPost: { edges: posts = [] },
    },
  } = response;

  // Build a new page that could list the blog posts
  createPage({
    path: '/blog',
    component: path.resolve('./src/templates/blog-post-list.js'),
    context: posts,
  });

  // Build a new page for each blog post
  posts.forEach(({ node: post }) => {
    createPage({
      path: `/blog/${post.slug}`,
      component: path.resolve('./src/templates/blog-post.js'),
      context: post,
    });
  });
};

Example Using Static Query

// ./gatsby-node.js
const path = require('path');

module.exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  // Sample query for all blog posts.
  const response = await graphql(`
    query GatsbyNodeQuery {
      allDirectusBlogPost {
        edges {
          node {
            directusId
          }
        }
      }
    }
  `);

  // Destructure response to get post IDs
  const {
    data: {
      allDirectusBlogPost: { edges: posts = [] },
    },
  } = response;

  // Build a new page for each blog post, passing the directusId
  // via `context` for the static query
  posts.forEach(({ node: post }) => {
    createPage({
      path: `/blog/${post.slug}`,
      component: path.resolve('./src/templates/blog-post.js'),
      context: post,
    });
  });
};
// ./src/templates/blog-post.js
import { graphql } from 'gatsby';
import React from 'react';

// A static query, the results from which
// will be passed to our component. Uses the 'directusId' property
// passed via the `createPage` context config to retrieve the blog post.
export const query = graphql`
  query($directusId: Int!) {
    directusBlogPost(directusId: {eq: $directusId}) {
      directusId
      title
      author
      body
      preview
      created
      slug
    }
  }
`;

// The component we'll render for a given blog post
const BlogPost = ({
  data: { directusBlogPost: post }
}) => {
  return (
    // ... Some markup consuming the 'post' data
  );

};

export default BlogPost;

Sample Using a Page Query

// ./src/pages/blog.js
import { graphql } from 'gatsby';
import React from 'react';

// The query used to provide the page data
// for the '/blog' route.
export const pageQuery = graphql`
    query BlogQuery {
      allDirectusBlogPost {
        edges {
          node {
            directusId
            title
            author
            body
            preview
            created
            slug
          }
        }
      }
    }
`;

// The component that will render a list of blog posts
const BlogPage = ({
    data,
    location
}) => {

  // Extracting post data from props.
  const {
      allDirectusBlogPost: posts = [],
  } = data;

  return (
    // ... Some markup consuming the list of 'posts'
  );

};

export default BlogPage;

Query Examples

Basic query for a list of blog posts

query BlogQuery {
  allDirectusBlogPost {
    edges {
      node {
        directusId
        title
        author
        body
        preview
        created
        slug
      }
    }
  }
}

Basic query for a single blog post (via directusId)

query BlogPostQuery($directusId: Int!) {
  directusBlogPost(directusId: {eq: $directusId}) {
    directusId
    title
    author
    body
    preview
    created
    slug
  }
}

Filtered & sorted list of blog posts

query BlogPostQuery {
  allDirectusBlogPost(
    filter: {created: {gte: "2020-01-01 00:00:00"}},
    sort: {order: DESC, fields: created}
  ) {
    edges {
      node {
        directusId
        title
        author
        body
        preview
        created
        slug
      }
    }
  }
}

Join for a list of products & all projects associated with each product.

Assumes a field related_projects exists as the join field on the product table in Directus.

query ProductQuery {
  allDirectusProduct {
    edges {
      node {
        directusId
        name
        created
        related_projects {
          directusId
          name
        }
      }
    }
  }
}

Join for the product category details via product ID.

Assumes a field owning_product_category exists as the join field on the product table in Directus.

query ProductQuery($directusId: Int!) {
  directusProduct(directusId: {eq: $directusId}) {
    directusId
    name
    created
    owning_product_category {
      directusId
      name
      description
    }
  }
}

Join for the images (stored as Directus files) associated with a product

Assumes a field images exists as the join field on the product table in Directus.

query ProductQuery($directusId: Int!) {
  directusProduct(directusId: {eq: $directusId}) {
    directusId
    name
    created
    images {
      id
      data {
        full_url
        thumbnails {
          height
          width
          dimension
          url
        }
      }
      description
      type
      title
    }
  }
}

Sample thumbnail query using gatsby-image

Builds a set of thumbnails (150px x 150px) for all images. Assumes downloadFiles was true in the plugin's config. The originalName property should match the corresponding directusFile filename property.

query AllImageThumbnails {
  allImageSharp {
    edges {
      node {
        sizes(maxHeight: 150, maxWidth: 150) {
          src
          originalName
        }
      }
    }
  }
}

Notes

Known Limitations

For the a collection type to exist in the GraphQL layer, there must be at least one record processed by the plugin belonging to the collection.

E.g. if either no records exist for the collection, or they are all filtered by the plugin configuration, that collection will not appear in the GraphQL layer, and any attempts to query against it will throw an error.

Similarly, for a field defined as a M2O, O2M, M2M, or File(s) relation to exist in the GraphQL schema for a given collection, there must exist at least one record that has a non-null value for that field. Attempts to query fields where no relations have been defined in Directus will throw an error.

E.g. if we have a products collection in Directus that has a reviews field defined as a O2M relation to a review collection, if all products have no reviews, the reviews field will not be available in the directusProducts GraphQL schema, and attempts to execute a query { directusProducts { reviews { ... } } } query will throw an error.

Development

The project is written in TypeScript. You can clone the repo and use the command npm run dev to start TypeScript in watch-mode. npm run build builds the project.

Release

Before making a new release, be sure that the project passes linting and the tests, otherwise it won't be published to NPM.

First, we will bump the package version number, commit the change and finally tag the commit with our new version. This is done with the help of the NPM version command (see npm help version).

npm run release [<newversion> | major | minor | patch]

Push the release commit and then the release tag to the Github repository:

git push --follow-tags

Github will take care of building the project, running all the tests, and finally publishing the package to the NPM registry.

The next step is to edit the auto generated release draft on Github to create a new release attached to the tag that we previously pushed.

The release draft may contain some useless changes, so remove any unnecessary logs from it. Once the release draft is ready, it can be published.