/graphql-relay-mongodb-pagination

Example of Relay-compatible pagination with MongoDB and Apollo Server 2

Primary LanguageJavaScriptMIT LicenseMIT

graphql-relay-mongodb-pagination

Example of Relay Cursor Connections with MongoDB and Apollo Server 2. Ported from reindexio/graphql-relay-mongodb-pagination that provided the code accompnaying the article Relay-compatible GraphQL pagination with MongoDB.

The example has been enhanced so that the ArticleConnection objects also support a nodes object like GitHub's GraphQL API does.

Also added are mutation and subscription with redis examples.

Running the example

Install npm dependencies:

yarn install

Start MongoDB and redis (unless you have them running locally already):

docker-compose up mongodb redis

Load sample data directly into MongoDB:

yarn load:mongo

This will load 1,000 articles into the Articles collection of the relaypagination database.

Install MongoDB Compass:

brew cask install mongodb-compass

Start the server:

yarn start:dev

image

Install GraphQL Playground:

brew cask install graphql-playground

image

Here is the text for the query highlighted in the screenshot:

query Last {
  viewer {
    id
  }
  articles(last: 2) {
    nodes {
      text
    }
    edges {
      cursor
      node {
        text
        id
        createdAt
        updatedAt
      }
    }
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
      endCursor
    }
  }
}

Articles For Viewer

{
  viewer {
    id
    username
    articles(last: 2) {
      nodes {
        id
        text
      }
      pageInfo {
        startCursor
        endCursor
        hasNextPage
        hasPreviousPage
      }
    }
  }
}
{
  "data": {
    "viewer": {
      "id": "5e00fcad8700880af081ab7b",
      "username": "janesmith",
      "articles": {
        "nodes": [
          {
            "id": "5e00fcad8700880af081ab8f",
            "text": "19 - Corporis voluptates magnam inventore sunt non itaque molestias."
          },
          {
            "id": "5e00fcad8700880af081ab90",
            "text": "20 - Sed harum nostrum nobis dolorum nesciunt."
          }
        ],
        "pageInfo": {
          "startCursor": "NWUwMGZjYWQ4NzAwODgwYWYwODFhYjhm",
          "endCursor": "NWUwMGZjYWQ4NzAwODgwYWYwODFhYjkw",
          "hasNextPage": false,
          "hasPreviousPage": true
        }
      }
    }
  }
}

Mutations

This repository contains an example of calling a mutation with the graphql-request to load sample data:

You can run this script with the following command:

yarn load:apollo-client

Here is an example of using graphql-request to add a single article.

import { request } from 'graphql-request';

const endpoint = 'http://localhost:4001/graphql';

const query = `
mutation AddArticle($article:AddArticleInput!) {
  addArticle(input:$article){
    article{
      id
      text
      createdAt
      updatedAt
    }
  }
}
`;

const variables = {
  article: {
    text: 'graphql-request mutation',
  },
};

// using request
//
request(endpoint, query, variables)
  .then(data => console.log(JSON.stringify(data, undefined, 2)))
  .catch(error => console.error(error));

Uploads

There are two mutations in the GraphQL schema that perform file uploads, singleUpload and multipleUpload. The following scripts can be used to test singleUpload and multipleUpload respectively:

  • scripts/singleUpload.sh
  • scripts/multipleUpload.sh
$ scripts/singleUpload.sh | jq
{
  "data": {
    "singleUpload": {
      "filename": "yarn.lock",
      "mimetype": "application/octet-stream",
      "encoding": "7bit"
    }
  }
}
$ scripts/multipleUpload.sh | jq
{
  "data": {
    "multipleUpload": [
      {
        "filename": "package.json",
        "mimetype": "application/octet-stream",
        "encoding": "7bit"
      },
      {
        "filename": "nodemon.json",
        "mimetype": "application/octet-stream",
        "encoding": "7bit"
      }
    ]
  }
}

Subscriptions

This project uses redis for subscriptions by utilizing the davidyaha/graphql-redis-subscriptions PubSub implementation.

Open two terminal windows and run docker compose up --build in one and yarn start:dev in the other. This will result in two instances of the Node.js GraphQL server running.

docker-compose up --build
yarn start:dev

Now open two GraphQL Playgroud IDE instances. One to http://localhost:4001/graphql and the other to http://localhost:4001/graphql.

Note that the Node.js graphql app running on port 4002 (from docker-compose) does not support playground since NODE_ENV is set to production. Thus the use of the GraphQL Playground IDE client.

In the first GraphQL Playground IDE instance enter this query and execute it:

subscription Articles {
  articleAdded {
    id
    text
    createdAt
    updatedAt
  }
}

In the second GraphQL Playground instance enter and execute this query:

mutation AddArticle($article: AddArticleInput!) {
  addArticle(input: $article) {
    article {
      id
      text
      createdAt
      updatedAt
    }
  }
}

With query variables:

{
  "article": {
    "text": "hello there"
  }
}

Montioring subscriptions with redis-cli:

$ redis-cli
127.0.0.1:6379> monitor
OK
1576084903.219562 [0 172.27.0.1:45834] "unsubscribe" "ARTICLE_ADDED"
1576084903.219608 [0 172.27.0.1:45834] "punsubscribe" "ARTICLE_ADDED"
1576084905.559995 [0 172.27.0.1:45834] "subscribe" "ARTICLE_ADDED"
1576084910.945354 [0 172.27.0.1:45830] "publish" "ARTICLE_ADDED" "{\"articleAdded\":{\"text\":\"hello there\",\"createdAt\":\"2019-12-11T17:21:50.919Z\",\"updatedAt\":\"2019-12-11T17:21:50.919Z\",\"_id\":\"5df125ae87f96d1c3a7d25bc\"}}"

apollo2-subscriptions

docker

The docker-compose.yaml file will by default build and use a container for the Node.js app. This can be time consuming during development. Running docker-compose up mongodb will just start a MongoDB container that can be used with yarn start:dev during development.

To start the Node.js app and MongoDB together do:

docker-compose up --build

It is important to include --build if there have been changes to the source code.

The Node.js app is available on localhost:4002 when running via docker-compose. Note that the playground is not available as the environment is set as production. Use GraphQL Playground to interact with GraphQL in this case.

Node.js and Docker

Node.js doesn't like running as pid 1. Use --init with docker run or Tini if your containers are headed to Kubernetes, since the --init flag isn't supported there.

References