Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/contentful/cf-graphql

Generate a GraphQL schema out of your Contentful space
https://github.com/contentful/cf-graphql

contentful graphql

Last synced: about 1 month ago
JSON representation

Generate a GraphQL schema out of your Contentful space

Awesome Lists containing this project

README

        

# cf-graphql

[![travis build status](https://img.shields.io/travis/contentful-labs/cf-graphql.svg)](https://travis-ci.org/contentful-labs/cf-graphql)
[![npm version](https://img.shields.io/npm/v/cf-graphql.svg)](https://www.npmjs.com/package/cf-graphql)
[![npm downloads](https://img.shields.io/npm/dt/cf-graphql.svg)](https://www.npmjs.com/package/cf-graphql)
[![deps status](https://img.shields.io/david/contentful-labs/cf-graphql.svg)](https://david-dm.org/contentful-labs/cf-graphql)
[![dev deps status](https://img.shields.io/david/dev/contentful-labs/cf-graphql.svg)](https://david-dm.org/contentful-labs/cf-graphql?type=dev)
[![codecov coverage](https://img.shields.io/codecov/c/github/contentful-labs/cf-graphql.svg)](https://codecov.io/gh/contentful-labs/cf-graphql)

`cf-graphql` is a library that allows you to query your data stored in [Contentful](https://www.contentful.com/) with [GraphQL](http://graphql.org/). A schema and value resolvers are automatically generated out of an existing space.

Generated artifacts can be used with any node-based GraphQL server. The outcome of the project's main function call is an instance of the [`GraphQLSchema`](http://graphql.org/graphql-js/type/#graphqlschema) class.

## Table of contents

- [Disclaimers](#disclaimers)
- [First steps](#first-steps)
- [Demo](#demo)
- [Run it locally](#run-it-locally)
- [Deploy to Zeit's now](#deploy-to-zeits-now)
- [Programmatic usage](#programmatic-usage)
- [Querying](#querying)
- [Helpers](#helpers)
- [Contributing](#contributing)

## Disclaimers

Please note that `cf-graphql` library is released as an experiment:

- we might introduce breaking changes into programmatic interfaces and space querying approach before v1.0 is released
- there’s no magic bullet: complex GraphQL queries can result in a large number of CDA calls, which will be counted against your quota
- we might discontinue development of the library and stop maintaining it

## First steps

If you just want to see how it works, please follow the [Demo](#demo) section. You can deploy the demo with your own credentials so it queries your own data.

In general `cf-graphql` is a library and it can be used as a part of your project. If you want to get your hands dirty coding, follow the [Programmatic usage](#programmatic-usage) section.

## Demo

We host an [online demo](https://cf-graphql-demo.now.sh/) for you. You can query Contentful's "Blog" space template there. This how its graph looks like:

![Demo space graph](./demo/demo-space-graph.png)

### Run it locally

This repository contains a demo project. The demo comes with a web server (with [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) enabled) providing the GraphQL, [an in-browser IDE (GraphiQL)](https://github.com/graphql/graphiql) and a React Frontend application using this endpoint.

To run it, clone the repository, install dependencies and start a server:

```
git clone [email protected]:contentful-labs/cf-graphql.git
cd cf-graphql/demo
# optionally change your node version with nvm, anything 6+ should work just fine
# we prefer node v6 matching the current AWS Lambda environment
nvm use
npm install
npm start
```

Use to query the data from within your application and navigate to to use the IDE (GraphiQL) for test-querying. Please refer to the [Querying](#querying) section for more details.

If you also want to see how to integrate GraphQL in a React technology stack the demo project also contains an application based on the [Apollo framework](https://www.apollodata.com/). To check it out use .

To use your own Contentful space with the demo, you have to provide:

- space ID
- CDA token
- CMA token

Please refer the ["Authentication" section](https://www.contentful.com/developers/docs/references/authentication/) of Contentful's documentation.

You can provide listed values with env variables:

```
SPACE_ID=some-space-id CDA_TOKEN=its-cda-token CMA_TOKEN=your-cma-token npm start
```

### Deploy to [Zeit's `now`](https://zeit.co/now)

To be able to deploy to [Zeit's `now`](https://zeit.co/now) you need to have an activated account. There is a free open source option available.

You can also deploy the demo with `now`. In your terminal, navigate to the `demo/` directory and run:

```
npm run deploy-demo-now
```

As soon as the deployment is done you'll have a URL of your GraphQL server copied.

You can also create a deployment for your own space:

```
SPACE_ID=some-space-id CDA_TOKEN=its-cda-token CMA_TOKEN=your-cma-token npm run deploy-now
```

Please note:

- when deploying a server to consume Contentful's "Blog" space template, the command to use is `npm run deploy-demo-now`; when the demo should be configured to use your own space, the command is `npm run deploy-now`
- if you've never used `now` before, you'll be asked to provide your e-mail; just follow on-screen instructions
- if you use `now`'s OSS plan (the default one), the source code will be public; it's completely fine: all credentials are passed as env variables and are not available publicly

## Programmatic usage

The library can be installed with `npm`:

```
npm install --save cf-graphql
```

Let's assume we've required this module with `const cfGraphql = require('cf-graphql')`. To create a schema out of your space you need to call `cfGraphgl.createSchema(spaceGraph)`.

What is `spaceGraph`? It is a graph-like data structure containing descriptions of content types of your space which additionally provide some extra pieces of information allowing the library to create a GraphQL schema.

To prepare this data structure you need to fetch raw content types data from the [CMA](https://www.contentful.com/developers/docs/references/content-management-api/). Let's create a Contentful client first:

```js
const client = cfGraphql.createClient({
spaceId: 'some-space-id',
cdaToken: 'its-cda-token',
cmaToken: 'your-cma-token'
});
```

`spaceId`, `cdaToken` and `cmaToken` options are required. You can also pass the following options:

- `locale` - a locale code to use when fetching content. If not provided, the default locale of a space is used
- `preview` - if `true`, CPA will be used instead of CDA for fetching content
- `cpaToken` - if `preview` is `true` then this option has to hold a CPA token

Fetch content types with your `client` and then pass them to `cfGraphql.prepareSpaceGraph(rawCts)`:

```js
client.getContentTypes()
.then(cfGraphql.prepareSpaceGraph)
.then(spaceGraph => {
// `spaceGraph` can be passed to `cfGraphql.createSchema`!
});
```

The last step is to use the schema with a server. A popular choice is [express-graphql](https://github.com/graphql/express-graphql). The only caveat is how the context is constructed. The library expects the `entryLoader` key of the context to be set to an instance created with `client.createEntryLoader()`:

```js
// Skipped in snippet: `require` calls, Express app setup, `client` creation.
// `spaceGraph` was fetched and prepared in the previous snippet. In most cases
// you shouldn't be doing it per request, once is fine.
const schema = cfGraphql.createSchema(spaceGraph);

// IMPORTANT: we're passing a function to `graphqlHTTP`: this function will be
// called every time a GraphQL query arrives to create a fresh entry loader.
// You can also use `expressGraphqlExtension` described below.
app.use('/graphql', graphqlHTTP(function () {
return {
schema,
context: {entryLoader: client.createEntryLoader()}
};
}));
```

[You can see a fully-fledged example in the `demo/` directory](./demo/server.js).

## Querying

For each Contentful content type three root-level fields are created:

- a singular field accepts a required `id` argument and resolves to a single entity
- a collection field accepts an optional `q`, `skip` and `limit` arguments and resolves to a list of entities
- a collection metadata field accepts an optional `q` argument and resolves to a metadata object (currently comprising only `count`)

Please note that:

- the `q` argument is a query string you could use with the [CDA](https://www.contentful.com/developers/docs/references/content-delivery-api/)
- both `skip` and `limit` arguments can be used to fetch desired page of results
* `skip` defaults to `0`
* `limit` defaults to `50` and cannot be greater than `1000`
* some query string parameters cannot be used:
* `skip`, `limit` - use collection field arguments instead
* `include`, `content_type` - no need for them, the library will determine and use appropriate values internally
* `locale` - all the content is fetched for a single locale. By default the default locale is used; alternate locale can be selected with the `locale` configuration option of `cfGraphql.createClient`

Assuming you've got two content types named `post` and `author` with listed fields, this query is valid:

```graphql
{
authors {
name
}

authors(skip: 10, limit: 10) {
title
rating
}

_authorsMeta {
count
}

posts(q: "fields.rating[gt]=5") {
title
rating
}

_postsMeta(q: "fields.rating[gt]=5") {
count
}

post(id: "some-post-id") {
title
author
comments
}
}
```

Reference fields will be resolved to:

- a specific type, if there is a validation that allows only entries of some specific content type to be linked
- the `EntryType`, if there is no such constraint. The `EntryType` is an interface implemented by all the specific types

Example where the `author` field links only entries of one content type and the `related` field links entries of multiple content types:

```graphql
{
posts {
author {
name
website
}

related {
... on Tag {
tagName
}
... on Place {
location
name
}
}
}
}
```

Backreferences (_backrefs_) are automatically created for links. Assume our `post` content type links to the `author` content type via a field named `author`. Getting an author of a post is easy, getting a list of posts by an author is not. `_backrefs` mitigate this problem:

```graphql
{
authors {
_backrefs {
posts__via__author {
title
}
}
}
}
```

When using backreferences, there is a couple of things to keep in mind:

- backrefs may be slow; always test with a dataset which is comparable with what you've got in production
- backrefs are generated only when a reference field specifies a single allowed link content type
- `_backrefs` is prefixed with a single underscore
- `__via__` is surrounded with two underscores; you can read this query out loud like this: _"get posts that link to author via the author field"_

## Helpers

`cf-graphql` comes with helpers that help you with the `cf-graphql` integration. These are used inside of [the demo application](https://github.com/contentful-labs/cf-graphql/tree/master/demo).

### `expressGraphqlExtension`

`expressGraphqlExtension` is a simple utility producing a function that can be passed directly to the [`express-graphql` middleware](https://github.com/graphql/express-graphql).

```javascript
// Skipped in this snippet: client and space graph creation
const schema = cfGraphql.createSchema(spaceGraph);

const opts = {
// display the current cf-graphql version in responses
version: true,
// include list of the underlying Contentful CDA calls with their timing
timeline: true,
// display detailed error information
detailedErrors: true
};

const ext = cfGraphql.helpers.expressGraphqlExtension(client, schema, opts);
app.use('/graphql', graphqlHTTP(ext));
```

**Important**: Most likely don't want to enable `timeline` and `detailedErrors` in your production environment.

### `graphiql`

If you want to run your own GraphiQL and don't want to rely on the one shipping with e.g. [express-graphql](https://github.com/graphql/express-graphql) then you could use the `graphiql` helper.

```javascript
const ui = cfGraphql.helpers.graphiql({title: 'cf-graphql demo'});
app.get('/', (_, res) => res.set(ui.headers).status(ui.statusCode).end(ui.body));
```

## Contributing

Issue reports and PRs are more than welcomed.

## License

MIT