Month: March 2019

  • What’s coming in v0.3.0?

    The next release of WPGraphQL will be version v0.3.0, and is slated to be released next week.

    This release is arguably the most substantial release to date. It’s going to made up of 2 major features: “DataLoader” and “Model Layer

    DataLoader

    The role of DataLoader is to load data as efficiently as possible. You can read some of the highlights in the PR comment here: https://github.com/wp-graphql/wp-graphql/pull/722#issue-261315185

    Model Layer

    The goal of the Model Layer is to centralize access control to objects and properties throughout the Graph. In short, that means we just want to make things more secure.

    At the moment, some potentially sensitive data is exposed by default, and it’s the site owner’s responsibility to filter and adjust the Schema and resolvers to only expose data they want to be exposed in the API. You can read more about that here (under the Sensitive Data heading): https://docs.wpgraphql.com/getting-started/users

    The Model Layer switches the plugins stance to be more restrictive by default, and allow site owners to loosen the restrictions when necessary, instead of expecting site owners to go through the effort of limiting potentially private content out of the box. Of course, each application is different, and our defaults may not be what you need for your application, so you will be able to filter access at a very granular level to make the GraphQL API work best for your specific needs.

    The Model Layer centralizes access checks by taking into consideration the “current_user” making the request, then determines what objects (Posts, Terms, Comments, etc) and fields of those objects (Title, Content, etc) should be exposed to the user making the request. Fields like user emails will now only be exposed by default to authenticated users with “list_users” capabilities, for example. If you want to make override that new default and expose a field that we have restricted, you’ll have the ability to control that via various filters.

    We’ll be working on a formal upgrade guide, as there will be numerous breaking changes for this release, but we think they’re all very welcome breaking changes.

    You can keep an eye on progress in the branches here:

    Security Audit

    Over the past weekend we received the results of a Security Audit performed by Simone Quatrini of Pen Test Partners. The security audit pointed out some Insecure Access Control violations. Most of the violations in the report had already been resolved by the in-progress Model Layer branch, such as exposed user email addresses, which, as mentioned above is documented here.

    There were 2 issues presented that we were not on our radar and we are grateful to Simone for bringing to our attention. We will be patching shortly as part of the v0.3.0 release. One issue, in particular, will be fixed for previous versions of the plugin as well, and details will be published about that once it has been resolved.

    Full details of the Security Audit and resolutions to the issues will be published after the v0.3.0 release and other mentioned patches have been released.

  • WPGraphQL at WordCamp Montreal


    Étienne Bélanger gave a talk at WordCamp Montreal 2018 titled: “Your First Headless WordPress Project with ReactJS and GraphQL”.

    In the talk, he covers some basic differences between the WP REST API and WPGraphQL, then shows how to use React and Apollo Client to consume data from a WordPress site with the WPGraphQL plugin active.

    Great talk, Étienne! – and thanks for sharing the video with us.

  • WPGraphQL + Gatsby Tutorial

    Zac Gordon put together a group of engineers to work on a formal project, GatsbyWPThemes.com to port popular WordPress themes to Gatsby themes.

    Recently, Muhammad Muhsin published a tutorial showcasing how users can create a Gatsby theme using WordPress as the CMS and WPGraphQL as the API for Gatsby to consume data from.

    Take a look at the tutorial, and let us know what you build with Gatsby and WPGraphQL!

  • Release v0.2.3

    Today we released a new version of WPGraphQL, v0.2.3.

    The release can be found here: https://github.com/wp-graphql/wp-graphql/releases/tag/v0.2.3

    Below is a recap of the release notes:

    Breaking Changes

    • none

    New Features

    • #676: New TimezoneEnum type added to the Schema
    • #719: New graphql_menu_item_connection_args and graphql_menu_item_connection_query_argsfilters added to the MenuItemConnectionResolver. Thanks @epeli!

    Bug Fixes

    • #714 Fixes global $post not properly being setup before resolving fields for posts. Thanks @epeli!
    • #716: Adjusts how the MenuLocationEnum values are determined to include all locations, not just locations with a menu already assigned.

  • Easy static HTML exports of your Next.js + GraphQL site

    You’re here because you’d like to learn how to create static HTML exports for your Next.js site which uses GraphQL as a data source to create dynamic pages from page components. And that site may even use WPGraphQL to pull content from WordPress.

    Fantastic, this article will describe the simple process of doing exactly that.

    Generally speaking, all else being equal, serving a static HTML file as a webpage is generally going to be the fastest way to get that page in your users hands. There are exceptions to every rule, but this is a pretty safe bet. This speed is great for SEO, UX, conversion rates, blah blah blah, but building a site that delivers that fast is also just fun!

    Bare with me for a few moments as we setup the solution with a bit of boilerplate.

    As you will see if you peruse the interactive Next.js docs, they have a simple method of enabling static HTML exports. Create a file in your project at the root directory called next.config.js, and add the following code:

    module.exports = {
      exportPathMap: function () {
        return {
          '/': { page: '/' }
        }
      }
    }

    Then add the following to package.json:

    {
      "scripts": {
        "build": "next build",
        "export": "next export"
      }
    }

    And then run:

    npm run build
    npm run export

    Now checkout the out directory. You’ll see static HTML file for your index route. You can now run the following to see your site in action:

    npm install -g serve
    cd out
    serve -p 8080

    The Next.js docs then extend this example with the following code snippet:

    module.exports = {
      exportPathMap: function () {
        return {
          '/': { page: '/' },
          '/about': { page: '/about' },
          '/p/hello-nextjs': {page: '/post', query: { title: 'Hello Next.js' } },
          '/p/learn-nextjs': { page: '/post', query: { title: 'Learn Next.js is awesome' } },
          '/p/deploy-nextjs': { page: '/post', query: { title: 'Deploy apps with Zeit' } },
          '/p/exporting-pages': { page: '/post', query: { title: 'Learn to Export HTML Pages' } }
        }
      }
    }

    What we are doing here on the lines starting with '/p…' is passing query data to the component at '/post'.

    Ok, now the problem becomes clear: we need a data source to populate the query (or params, title, date, etc.) that our components rely on. But the next export doesn’t “hook into” the GraphQL data source you have likely already setup in your _app.js.

    So does this mean you need to rewire everything to hook up GraphQL, write fetching logic, looping, etc?

    No, it means I had to. You can just install the NPM package I created and do some basic configuration. 🙂

    Enter next-graphql-static-export:

    A minimal package which provides the functionality required to perform a static HTML export of dynamic pages from Next.js when using GraphQL as a data source.
    https://www.npmjs.com/package/next-graphql-static-export

    Ok, end of boilerplate setup. Let’s get down creating your Next.js+GraphQL+WPGraphQL+static-HTML-exported site.

    By the way, you do not need to include WPGraphQL in your project to make use of this method. Though if your site has a WordPress backend, you probably should.

    There are no assumptions in the code for the NPM package which rely on WPGraphQL, other than possibly the schema.

    Here is the relevant bit of the WPGraphQL schema in a query result:

    {
      "data": {
        "posts": {
          "pageInfo": {
            "endCursor": "PQOXYXljb25uZWN0oT6uOjEwMzg5Mg==",
            "hasNextPage": true
          },
          "nodes": [
            {
              "id": "cG3zdDozMDO3MTA=",
              "uri": "why-you-should-use-wpgraphql",
              "title": "Why You Should Use WPGraphQL"
            }
          ]
        }
      }
    }

    Notice the nesting of pageInfo, endCursor and hasNext page, along with nodes. It’s worth taking a quick look at the results you get from queries on your project if you are not using WPGraphQL. If your GraphQL server does not return data in this format, no problem, you can still easily use this package. You’ll just need to write a custom query result parsing function. Details below.

    We’ll assume for the moment that your GraphQL server does have the same schema as WPGraphQL and move on.

    First install ‘next-graphql-static-export’:

    npm i next-graphql-static-export

    Now create a new config-exports.js file in the root directory of your project, where we will configure the parameters that we pass to the processContent made available via the next-graphql-static-export library. The following is an example of sensible defaults:

    // Include our queries. See https://docs.wpgraphql.com/getting-started/posts for basic queries
    const postsQuery = require("./queries/posts-query");
    const pagesQuery = require("./queries/pages-query");
    const productsQuery = require("./queries/products-query");
    
    // Your graphql endpoint
    const endpoint = `https://www.website.com/graphql`;
    
    // The custom information we pass to properly fetch data for each 'post type' or content type, the result of which is the object that Next.js needs to produce an accurate static HTML export of our site
    const typeParams = [
      {
        pageComponent: "page",
        contentType: "pages",
        query: pagesQuery,
        urlBase: "pages",
        perPage: 100,
        endpoint
      },
      {
        pageComponent: "product",
        contentType: "products",
        query: productsQuery,
        urlBase: "products",
        perPage: 100,
        endpoint
      },
      {
        pageComponent: "article",
        contentType: "posts",
        query: postsQuery,
        urlBase: "articles",
        perPage: 100,
        endpoint
      }
    ];
    
    module.exports = {
      typeParams
    };

    Open up your package.json again, and add change the line pertaining to the dev script to the following:

    "scripts": {
        "dev": "EXPORT=false node server.js",
        ...
    }

    This EXPORT env var will allow us to continue to use npm run dev to develop our site, yet avoid doing a full static HTML export every time we change a line of code (see next step for where we use this env var).

    Open up your next.config.js again, and make it look similar to the following:

    const processContent = require("next-graphql-static-export");
    const { typeParams } = require("./config-export");
    
    module.exports = {
      exportPathMap: async () => {
        if (EXPORT === "false") return {}; // This is where we use the env var we just added to the dev script.
    
        const [pages, products] = await processContent(typeParams);
    
        // Create the static pages with Next
        return {
          "/": { page: "/" }
          // ...pages,
          // ...products
          // ...posts
        };
      }
    };

    Now go ahead and run the following again:

    npm run build
    npm run export
    cd out
    serve -p 8080

    Open up localhost:8080 and you should see your beautiful site with all dynamic page fully rendered and served as static HTML!

    Passing your own query result parsing function

    If your schema differs from the one shown above, then you will need to pass your own query result parsing function to your typeParams in your config-export.js file. Here is an example function which exactly reproduces the existing functionality:

    const parseQueryResults = (queryResponse, contentType) => {
      const {
        [contentType]: {
          nodes,
          pageInfo: { hasNextPage, endCursor } 
        }
      } = queryResponse;
      return { nodes, hasNextPage, endCursor };
    };

    Note that the function takes in the queryResponse, and the current contentType, both of which are used to destructure the query result and return the following required values: nodes, hasNextPage, endCursor.

    You can use any amount of logic in this function, but those values are required.

    To use this function in your implementation, simply add the function as a parameter to the relevant content types. So our new config-exports.js would look like this:

    // Include our queries. See https://docs.wpgraphql.com/getting-started/posts for basic queries
    const postsQuery = require("./queries/posts-query");
    const pagesQuery = require("./queries/pages-query");
    const productsQuery = require("./queries/products-query");
    
    // Your graphql endpoint
    const endpoint = `https://www.website.com/graphql`;
    
    // Custom query result parsing function
    const parseQueryResults = (queryResponse, contentType) => {
      const {
        [contentType]: {
          nodes,
          pageInfo: { hasNextPage, endCursor } 
        }
      } = queryResponse;
      return { nodes, hasNextPage, endCursor };
    };
    
    // The custom information we pass to properly fetch data for each 'post type' or content type, the result of which is the object that Next.js needs to produce an accurate static HTML export of our site
    const typeParams = [
      {
        pageComponent: "page",
        contentType: "pages",
        query: pagesQuery,
        urlBase: "pages",
        perPage: 100,
        endpoint,
        parseQueryResults // Passing our function as a parameter
      },
      {
        pageComponent: "product",
        contentType: "products",
        query: productsQuery,
        urlBase: "products",
        perPage: 100,
        endpoint,
        parseQueryResults
      },
      {
        pageComponent: "article",
        contentType: "posts",
        query: postsQuery,
        urlBase: "articles",
        perPage: 100,
        endpoint,
        parseQueryResults
      }
    ];
    
    module.exports = {
      typeParams
    };

    And that’s all there is to adapting this module to a different schema than the one natively expected. Feel free to reach out with any questions.