PHPackages                             fieldwork/craftql - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. fieldwork/craftql

ActiveCraft-plugin[API Development](/categories/api)

fieldwork/craftql
=================

A GraphQL implementation for Craft

1.3.3(7y ago)012JavaScript

Since Dec 7Pushed 7y ago2 watchersCompare

[ Source](https://github.com/EMT/craftql)[ Packagist](https://packagist.org/packages/fieldwork/craftql)[ RSS](/packages/fieldwork-craftql/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (2)Dependencies (3)Versions (9)Used By (0)

[![CraftQL seen through the GraphiQL UI](https://raw.githubusercontent.com/markhuot/craftql/master/assets/graphiql.png)](https://raw.githubusercontent.com/markhuot/craftql/master/assets/graphiql.png)

[![Build Status](https://camo.githubusercontent.com/e48efdfcc5498dafb0eefdd07f2fba9aa2863db7682371c1b6f369e63b159d06/68747470733a2f2f7472617669732d63692e6f72672f6d61726b68756f742f6372616674716c2e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/markhuot/craftql)

A drop-in [GraphQL](http://graphql.org) server for your [Craft CMS](https://craftcms.com/) implementation. With zero configuration, *CraftQL* allows you to access all of Craft's features through a familiar GraphQL interface.

---

Examples
--------

[](#examples)

Once installed, you can test your installation with a simple Hello World,

```
{
  helloWorld
}
```

If that worked, you can now query Craft CMS using almost the exact same syntax as your Twig templates.

```
{
  entries(section:[news], limit:5, search:"body:salty") {
    ...on News {
      title
      url
      body
    }
  }
}
```

*CraftQL* provides a top level `entries` field that takes the same arguments as `craft.entries` does in your template. This is the most commonly used field/access point. E.g.,

```
query fetchNews {             # The query, `query fetchNews` is completely optional
  entries(section:[news]) {   # Arguments match `craft.entries`
    ...on News {              # GraphQL is strongly typed, so you must specify each Entry Type you want data from
      id                      # A field to return
      title                   # A field to return
      body                    # A field to return
    }
  }
}
```

Types are automatically created for every Entry Type in your install. If you have a section named `news` and an entry type named `news` the GraphQL type will be named `News`. If you have a section named `news` and an entry type named `pressRelease` the GraphQL type will be named `NewsPressRelease`. The convention is to mash the section handle and the entry type handle together, unless they are the same, in which case the section handle will be used.

```
query fetchNews {
  entries(section:[news]) {
    ...on News {              # Any fields on the News entry type
      id
      title
      body
    }
    ...on NewsPressRelease {  # Any fields on the Press Release entry type
      id
      title
      body
      source
      contactInfo
      downloads {
        title
        url
      }
    }
  }
}
```

To modify content make sure your token has write access and then use the top level `upsert{EntryType}` `Mutation`. `upsert{EntryType}` takes arguments for each field defined in Craft.

```
mutation createNewEntry($title:String, $body:String) {
  upsertNews(
    title:$title,
    body:$body,
  ) {
    id
    url
  }
}
```

The above would be passed with variables such as,

```
{
  "title": "My first mutation!",
  "body": "Here's the body of my first mutation",
}
```

Matrix Fields
-------------

[](#matrix-fields)

Working with Matrix Fields are similar to working with Entry Types: if you have a Matrix Field with a handle of `body`, the containing Block Types are named `Body` + the block handle. For instance `BodyText` or `BodyImage`. You can use the key `__typename` from the resulting response to map over the blocks and display the appropriate component.

```
{
  entries(section: [news]) {
    ... on News {
      id
      title
      body {                  # Your Matrix Field
        ... on BodyText {     # Block Type
          __typename          # Ensures the response has a field describing the type of block
          blockHeading        # Fields on Block Type, uses field handle
          blockContent        # Fields on Block Type, uses field handle
        }
        ... on BodyImage {    # Block Type
          __typename          # Ensures the response has a field describing the type of block
          blockDescription    # Fields on Block Type, uses field handle
          image {             # Fields on Block Type, uses field handle
            id                # Fields on image field on Block Type, uses field handles
          }
        }
      }
    }
  }
}
```

Dates
-----

[](#dates)

All Dates in *CraftQL* are output as `Timestamp` scalars, which represent a unix timestamp. E.g.,

```
{
  entries {
    dateCreated  # outputs 1503368510
  }
}
```

Dates can be converted to a human friendly format with the `@date` directive,

```
{
  entries {
    dateCreated @date(as:"F j, Y") # outputs August 21, 2017
  }
}
```

Relationships
-------------

[](#relationships)

Related entries can be fetched in several ways, depending on your needs.

Similar to `craft.entries.relatedTo(entry)` you can use the `relatedTo` argument on the `entries` top level query field. For example, if you have a `Post` with an ID of `63` that is related to comments you could use the following.

```
{
  entries(relatedTo:[{element:63}], section:comments) {
    ...on Comments {
      id
      author {
        name
      }
      commentText
    }
  }
}
```

Note, the `relatedTo:` argument accepts an array of relations. By default `relatedTo:` looks for elements matching *all* relations. If you would like to switch to elements relating to *any* relation you can use `orRelatedTo:`.

The above approach, typically, requires separate requests for the source content and the related content. That equates to extra HTTP requests and added latency. If you're using the "connection" approach to CraftQL you can fetch relationships in a single request using the `relatedEntries` field of the `EntryEdge` type. The same request could be rewritten as follows to grab both the post and the comments in a single request.

```
{
  entriesConnection(id:63) {
    edges {
      node {
        ...on Post {
          title
          body
        }
      }
      relatedEntries(section:comments) {
        edges {
          node {
            ...on Comment {
              author {
                name
              }
              commentText
            }
          }
        }
      }
    }
  }
}
```

Transforms
----------

[](#transforms)

You can ask CraftQL for image transforms by specifying an argument to any asset field. Note: for this to work the volume storing the image must have "public URLs" enabled in the volume settings otherwise CraftQL will return `null` values.

If you have defined named transforms within the Craft UI you can reference the transform by its handle,

```
{
  entries {
    ...on Post {
      imageFieldHandle {
        thumbnail: url(transform: thumb)
      }
    }
  }
}
```

You can also specify the exact crop by using the `crop`, `fit`, or `stretch` arguments as specified in the [Craft docs](https://craftcms.com/docs/image-transforms).

```
{
  entries {
    ...on Post {
      imageFieldHandle {
        poster: url(crop: {width: 1280, height: 720, position: topLeft, quality: 50, format: jpg})
      }
    }
  }
}
```

Drafts
------

[](#drafts)

Drafts are best fetched through an edge node on the `entriesConnection` query. You can get all drafts for an entry with the following query,

```
{
  entriesConnection(id:63) {
    edges {
      node { # the published node, as `craft.entries` would return
        id
        title
      }
      drafts { # an array of drafts
        edges {
          node { # the draft content
            id
            title
            ...on Post { # draft fields are still referenced by entry type, as usual
              body
            }
          }
          draftInfo { # the `draftInfo` field returns the meta data about the draft
            draftId
            name
            notes
          }
        }
      }
    }
  }
}
```

Categories and Tags
-------------------

[](#categories-and-tags)

Taxonomy can be queried through the top level `categories` or `tags` field. Both work identically to their [`craft.entries`](https://craftcms.com/docs/templating/craft.entries) and [`craft.tags`](https://craftcms.com/docs/templating/craft.tags) counterparts.

```
{
  categories { # lists all categories, or use `tags` to get all tags
    id
    title
  }
}
```

For added functionality query categories and tags through their related `Connection` fields. This provides a spot in the return to get related entries too,

```
{
  categoriesConnection {
    totalCount
    edges {
      node {
        title   # the category title
      }
      relatedEntries {
        entries {
          title # an entry title, that's related to this category
        }
      }
    }
  }
}
```

Users
-----

[](#users)

Users can be queried via a top-level `users` field,

```
{
  users {
    id
    name
    email
  }
}
```

You can also mutate users via the `upsertUser` field. When passed an `id:` it will update the user. If the `id:` attribute is missing it will create a new user,

```
mutation {
  upsertUser(id:1, firstName:"Mark", lastName:"Huot") {
    id
    name # returns `Mark Huot` after the mutation
  }
}
```

Permissions can be set as well, but you must *always* pass the full list of permissions for the user. E.g.,

```
mutation {
  upsertUser(id:1, permissions:["accessCp","editEntries:17","createEntries:17","deleteEntries:17"]) {
    id
    name # returns `Mark Huot` after the mutation
  }
}
```

Security
--------

[](#security)

CraftQL supports GraphQl field level permissions. By default a token will have no rights. You must click into the "Scopes" section to adjust what each token can do.

[![token scopes](https://raw.githubusercontent.com/markhuot/craftql/master/assets/scopes.png)](https://raw.githubusercontent.com/markhuot/craftql/master/assets/scopes.png)

Scopes allow you to configure which GraphQL fields and entry types are included in the schema.

Third-pary Field Support
------------------------

[](#third-pary-field-support)

To add CraftQL support to your third-party field plugin you will need to listen to the `craftQlGetFieldSchema` event. This event, triggered on your custom field, will pass a "schema builder" into the event handler, allowing you to specify the field schema your custom field provides. For example, in your plugin's `::init` method you could specify,

```
Event::on(\my\custom\Field::class, 'craftQlGetFieldSchema', function (\markhuot\CraftQL\Events\GetFieldSchema $event) {
  // the custom field is passed as the event sender
  $field = $event->sender;

  // the schema exists on a public property of the event
  $event->schema

    // you can add as many fields as you need to for your field. Typically you'll
    // pass your field in, which will automatically set the name and description
    // based on the Craft config.
    ->addStringField($field);

  // the schema is a fluent builder and can be chained to set multiple properties
  // of the custom field
  $event->schema->addEnumField('customField')
    ->lists()
    ->description('This is a custom description for the field')
    ->values(['KEY' => 'Label', 'KEY2' => 'Another label']);
});
```

The above, when called for a Post entry type on the `excerpt` field would generate a schema approximately equlilivant to,

```
type CustomFieldEnum {
  # Label
  KEY

  # Another label
  KEY2
}

type Post {
  # The field instructions are automatically included
  excerpt: String

  # This is a custom description for the field
  customField: [CustomFieldEnum]
}
```

If your custom field resolves an object you can expose that to CraftQL as well. For example, if you are implementing a custom field that exposes a map, with a latitude, longitute, and a zoom level, it may look like,

```
Event::on(\craft\base\Field::class, 'craftQlGetFieldSchema', function ($event) {
  $field = $event->sender;

  $object = $event->schema->createObjectType('MapPoint')
        ->addStringField('lat')
        ->addStringField('lng')
        ->addStringField('zoom');

  $event->schema->addField($field)->type($object);
});
```

Roadmap
-------

[](#roadmap)

No software is ever done. There's a lot still to do in order to make *CraftQL* feature complete. Some of the outstanding items include,

- Matrix fields are not included in the schema yet
- Table fields are not included in the schema yet
- Asset mutations (implemented by passing a URL or asset id)
- File uploads to assets via POST $\_FILES during a mutation
- Automated testing is not functional yet
- Automated testing doesn't actually *test* anything yet
- Mutations need a lot more testing
- `relatedEntries:` improvements to take source/target
- [Persisted queries](https://github.com/markhuot/craftql/issues/10)
- [Subclassed enum fields](https://github.com/markhuot/craftql/issues/40) that are able to return the raw field value

Requirements
------------

[](#requirements)

- Craft 3.0.0-RC1
- PHP 7.0+

Installation
------------

[](#installation)

If you don't have Craft 3 installed yet, do that first:

```
$ composer create-project craftcms/craft my-awesome-site -s beta
```

Once you have a running version of Craft 3 you can install *CraftQL* with Composer:

```
$ composer require markhuot/craftql:^1.0.0
```

Running the CLI server
----------------------

[](#running-the-cli-server)

*CraftQL* ships with a PHP-native web server. When running *CraftQL* through the provided web server the bootstrapping process will only happen during the initial start up. This has the potential to greatly speed up responses times since PHP will persist state between requests. In general, I have seen performance improvements of 5x (500ms to &lt;100ms).

Caution: this can also create unintended side effects since Craft is not natively built to run this way. Do not use this in production it could lead to memory leaks, server fires, and IT pager notifications :).

```
php craft craftql/server

```

###  Health Score

29

—

LowBetter than 59% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity5

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 96.4% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~16 days

Total

7

Last Release

2615d ago

Major Versions

1.3.1 → 2.0.0-beta12019-02-05

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/334177?v=4)[Andy Gott](/maintainers/andygott)[@andygott](https://github.com/andygott)

---

Top Contributors

[![markhuot](https://avatars.githubusercontent.com/u/48975?v=4)](https://github.com/markhuot "markhuot (427 commits)")[![andygott](https://avatars.githubusercontent.com/u/334177?v=4)](https://github.com/andygott "andygott (7 commits)")[![brandonkelly](https://avatars.githubusercontent.com/u/47792?v=4)](https://github.com/brandonkelly "brandonkelly (1 commits)")[![Dean177](https://avatars.githubusercontent.com/u/4590254?v=4)](https://github.com/Dean177 "Dean177 (1 commits)")[![fredrik-sogaard](https://avatars.githubusercontent.com/u/764318?v=4)](https://github.com/fredrik-sogaard "fredrik-sogaard (1 commits)")[![grumpyoldman-io](https://avatars.githubusercontent.com/u/5748147?v=4)](https://github.com/grumpyoldman-io "grumpyoldman-io (1 commits)")[![narration-sd](https://avatars.githubusercontent.com/u/247945?v=4)](https://github.com/narration-sd "narration-sd (1 commits)")[![phoob](https://avatars.githubusercontent.com/u/1475800?v=4)](https://github.com/phoob "phoob (1 commits)")[![sebastian-lenz](https://avatars.githubusercontent.com/u/2273359?v=4)](https://github.com/sebastian-lenz "sebastian-lenz (1 commits)")[![woro83c](https://avatars.githubusercontent.com/u/7939054?v=4)](https://github.com/woro83c "woro83c (1 commits)")[![angrybrad](https://avatars.githubusercontent.com/u/61869?v=4)](https://github.com/angrybrad "angrybrad (1 commits)")

---

Tags

cmsCraftcraftcmscraft-plugin

### Embed Badge

![Health badge](/badges/fieldwork-craftql/health.svg)

```
[![Health](https://phpackages.com/badges/fieldwork-craftql/health.svg)](https://phpackages.com/packages/fieldwork-craftql)
```

###  Alternatives

[markhuot/craftql

A GraphQL implementation for Craft

31844.7k](/packages/markhuot-craftql)[wrav/oembed

A simple plugin to extract media information from websites, like youtube videos, twitter statuses or blog articles.

36205.0k3](/packages/wrav-oembed)[craftpulse/craft-typesense

Craft Plugin that synchronises with Typesense

122.7k](/packages/craftpulse-craft-typesense)[jsmrtn/craftagram

Grab Instagram content through the Instagram API

141.3k](/packages/jsmrtn-craftagram)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
