API fields are based on files in /app/graphql/types/**, but converted to camelCase.

PlaceCal/app/graphql/types at main · geeksforsocialchange/PlaceCal

Data Structures

Either specified by the query with these fields or returned from the server in this structure

PingType: String

NeighbourhoodType {
  name: String
  abbreviatedName: String
  unit: String
  unitName: String
  unitCodeKey: String
  unitCodeValue: String
}

ContactType {
  name: String
  email: String
  telephone: String
}

OpeningHoursType {
  dayOfWeek: String
  opens: String
  closes: String
}

AddressType {
  streetAddress: String
  postalCode: String
  addressLocality: String
  addressRegion: String
  neighbourhood: NeighbourhoodType
}

PartnerType {
  id: ID
  name: String
  summary: String
  description: String
  accessibilitySummary: String
  logo: String
  url: String
  facebookUrl: String
  twitterUrl: String
  address: AddressType
  areasServed: [NeighbourhoodType]
  contact: ContactType
  openingHours: OpeningHoursType
	articles: [Article]
}

EventType {
  id: ID
  name: String
  summary: String
  description: String
  startDate: ISO88601Format
  endDate: ISO88601Format
  address: AddressType
  organizer: PartnerType
}

ArticleType {
  name: String
  headline: String
  text: String
  articleBody: String
  datePublished: ISO88601Format
  dateCreated: ISO88601Format
  dateUpdated: ISO88601Format
  providers: [Partners]
}

Making queries

Currently we only serve read queries and no mutations. The examples are written in javascript.

The following code is a bare-bones query wrapper using the node-fetch npm module.

import fetch from 'node-fetch';

const endPoint = '<http://192.168.0.39:3000/api/v1/graphql>';

function query (query, variables) {
  return new Promise((resolve, reject) => {
    fetch(endPoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
      body: JSON.stringify({
        variables,
        query
      })
    })
    .then(r => r.json())
    .then(data => resolve(data))
  });
}

Ping

/* 
An empty call that tests if you're code is working and responds
with a simple message.
*/

query("{ping}")
  .then( (response) => {
    console.log(response);
  });

/* response */
{
  data: { ping: 'Hello World! The time is 2022-03-21 11:10:20 +0000' }
}

Event (show)

let queryString = `
  query Event($id: ID!) { 
    event(id: $id) { 
      id
      name
      summary
      description
      startDate
      endDate
      address {
        streetAddress
        postalCode
        addressLocality
        addressRegion
      }
      organizer {
        id
        name
      }
    }
  }`;

query(queryString, { id: 2 })
  .then( (response) => {
    console.log(JSON.stringify(response, null, 2));
  });

/* response */

{
  "data": {
    "event": {
      "id": "2",
      "name": "event 0: A summary of an event",
      "summary": "event 0: A summary of an event",
      "description": "A longer description of this event",
      "startDate": "2022-02-10 00:00:00 +0000",
      "endDate": null,
      "address": {
        "streetAddress": "123 A Street, A place",
        "postalCode": "SE2 0QG",
        "addressLocality": "Belvedere",
        "addressRegion": "London (region)"
      },
      "organizer": null
    }
  }
}

Events (index)

Shows a set of events in managable chunks. Can be used to load events on demand for pagination/infinite scroll. It’s a bit tricky so there’s an explanation of how connections work.

let queryString = `
  query EventConnection($first: Int, $after: String) { 
    eventConnection(first: $first, after: $after) {
      pageInfo {
        hasNextPage
				endCursor
      }
      edges {
        cursor
        node {
          id
          name
          summary
          description
          startDate
          endDate
          address {
            streetAddress
            postalCode
            addressLocality
            addressRegion
          }
          organizer {
            id
            name
          }
        }
      }
    }
  }`;

/* return the first block of events */
query(queryString, {first: 5})
  .then( (response) => {
    console.log(JSON.stringify(response, null, 2));
  });

/* first response */
"data": {
    "eventConnection": {
      "pageInfo": {
        "hasNextPage": true
      },
      "edges": [
        {
          "cursor": "MQ",
          "node": {
            "id": "2",
            "name": "event 0: A summary of an event",
            "summary": "event 0: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        },
        {
          "cursor": "Mg",
          "node": {
            "id": "3",
            "name": "event 1: A summary of an event",
            "summary": "event 1: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        },
        {
          "cursor": "Mw",
          "node": {
            "id": "4",
            "name": "event 2: A summary of an event",
            "summary": "event 2: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        },
        {
          "cursor": "NA",
          "node": {
            "id": "5",
            "name": "event 3: A summary of an event",
            "summary": "event 3: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        },
        {
          "cursor": "NQ",
          "node": {
            "id": "6",
            "name": "event 4: A summary of an event",
            "summary": "event 4: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        }
      ]
    }
  }
}

/*
Notes on connection pagination:
Cursors are GraphQL's way of identifying a record in a connection.
They act like IDs but are more complicated for reasons.

Input parameters
  first: limit the number of records to return
  after: only fetch records that follow on from this cursor

In the response each record is wrapped in an 'edge' that contains 
the GQL cursor identifier.

pageInfo:
  hasNextPage: (boolean) there are records that follow from this block
  endCursor: (string) the cursor of the last record retrieved 
*/

/* then to get the next block of events */
query(queryString, {first: 5, after: "NQ"})
  .then( (response) => {
    console.log(JSON.stringify(response, null, 2));
  });

/* second response */
{
  "data": {
    "eventConnection": {
      "pageInfo": {
        "hasNextPage": true
      },
      "edges": [
        {
          "cursor": "Ng",
          "node": {
            "id": "7",
            "name": "event 5: A summary of an event",
            "summary": "event 5: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        },
        {
          "cursor": "Nw",
          "node": {
            "id": "8",
            "name": "event 6: A summary of an event",
            "summary": "event 6: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        },
        {
          "cursor": "OA",
          "node": {
            "id": "9",
            "name": "event 7: A summary of an event",
            "summary": "event 7: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        },
        {
          "cursor": "OQ",
          "node": {
            "id": "10",
            "name": "event 8: A summary of an event",
            "summary": "event 8: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        },
        {
          "cursor": "MTA",
          "node": {
            "id": "11",
            "name": "event 9: A summary of an event",
            "summary": "event 9: A summary of an event",
            "description": "A longer description of this event",
            "startDate": "2022-02-10 00:00:00 +0000",
            "endDate": null,
            "address": {
              "streetAddress": "123 A Street, A place",
              "postalCode": "SE2 0QG",
              "addressLocality": "Belvedere",
              "addressRegion": "London (region)"
            },
            "organizer": null
          }
        }
      ]
    }
  }
}

Events (filter)

Events can be filtered by date range, neighbourhood or tag. All events are returned in ascending order of start date.

let queryString = `
  query EventsByFilter(
    $fromDate: String, 
    $toDate: String, 
    $neighbourhoodId: Int, 
    $tagId: Int) 
  {

    eventsByFilter(
      fromDate: $fromDate, 
      toDate: $toDate, 
      neighbourhoodId: $neighbourhoodId, 
      tagId: $tagId) 
    { 
      id
      name
      summary
      description
      startDate
      endDate
      address {
        streetAddress
        postalCode
        addressLocality
        addressRegion
      }
      organizer {
        id
        name
      }
    }
  }`;

/* 
notes on event filter
The filter has the following parameters:
  fromDate: the initial start point. defaults to 
	  date request was made. can be in the past.
    format is YYYY-MM-DD HH:MM
  toDate: the end limit of query. must be after fromDate
  neighbourhoodId: scope events to be in a 
    neighbourhood. returns events in BOTH
    the address and service area.
  tagId: scope events to only one tag
*/

query(queryString)
  .then( (response) => {
    console.log(JSON.stringify(response, null, 2));
  });

/* response */
{
  "data": {
    "eventsByFilter": []
  }
}