This example illustrates how easy and fast it is to build a backend for a civic issue tracker app using Meldio.
Meldio is an open source GraphQL backend for building delightful mobile and web apps. See our start building guide for detailed instructions on getting started with Meldio.
Need help?
First, you will need to install Meldio following these instructions
Then, clone this example from Github using the following command:
git clone https://github.com/meldio/civic-issue-tracker.gitNext, you will need to create new Facebook and Google OAuth applications and obtain App ID and Secret from both Facebook and Google.
To initialize Meldio, run the following command and follow the prompts. Simply accept defaults and enter Facebook and Google OAuth App ID and Secret when prompted for those.
cd civic-issue-tracker
meldio initStart meldio from the civic-issue-tracker directory with:
meldio runYou can now access Meldio Query IDE at http://localhost:9000/graphql.
The app allows users to see a list of issues reported in their city or town, promote issues (by liking them) and comment on the issues they care about. In addition, users can report new issues and close resolved issues.
The following mutations are provided:
-
addIssue - adds a new issue and connects it to user's profile (and city once geolocation features are built into Meldio). Returns
issueEdgeandviewerto allow the app to retrieve issue and current user data following the mutation. -
editIssue - allows owner of the issue to change headline, description or add a photo related to the issue. Returns
issueto allow the app to retrieve the updated information. -
closeIssue - allows owner of the issue to close the issue once it has been successfully resolved by the authorities. Returns
issueto allow the app to retrieve the updated information. -
addComment - Adds a comment to the issue identified by
issueIdparameter. ReturnscommentEdgeto allow the app to retrieve the added comment information andissueto potentially refresh the aggregates on the issue (e.g. number of comments). -
editComment - allows the user who made the comment to edit the text of the comment or potentially add picture or document attachments. Returns
commentto allow the app to retrieve the updated comment information. -
deleteComment - allows the user who made the comment to delete their comment. Returns
deletedCommentIdto allow the app to remove the comment andissueto potentially refresh the aggregates on the issue (e.g. number of comments). -
like - allows users to promote an issue by liking it. Returns
likeEdgeto allow the app to add the like into the list andissueto potentially refresh the aggregates on the issue (e.g. number of likes). -
unlike - allows the user to unlike an issue they previously liked. Returns
deletedLikeUserIdto allow the app to remove user's like from the list andissueto potentially refresh the aggregates on the issue. -
tagAgency - allows users to tag civic agency or service that is responsible for addressing the issue. Returns
agencyEdgeto allow the app to add the like into the list andissueto potentially refresh the aggregates on the issue. -
untagAgency - allows the user to untag an agency they previously tagged. Returns
untaggedAgencyIdto allow the app to remove the agency tag from the list andissueto potentially refresh the aggregates on the issue.
mutation AddIssue {
addIssue(input: {
category: MISSING_MANHOLE_COVER
headline: "On south east corner of 46th & 2nd"
description: "Manhole cover is missing on the south east corner of 46th street and 2nd avenue, right by the Starbucks entrance."
location: [40.7522576, -73.9703193]
photoUrl: "https://...url to your cdn..."
clientMutationId: "1"
}) {
issueEdge {
node {
id
category
headline
description
location
photoUrl
status
timestamp
}
}
}
}Mutations returns:
{
"data": {
"addIssue": {
"issueEdge": {
"node": {
"id": "-KGABwWFqW6FAQ5YSbb2-ZJJL5",
"category": "MISSING_MANHOLE_COVER",
"headline": "On south east corner of 46th & 2nd",
"description": "Manhole cover is missing on the south east corner of 46th street and 2nd avenue, right by the Starbucks entrance.",
"location": [
40.7522576,
-73.9703193
],
"photoUrl": "https://...url to your cdn...",
"status": "OPEN",
"timestamp": "2016-04-25T02:16:05.445Z"
}
}
}
}
}mutation AddComment {
addComment(input: {
issueId: "-KGABwWFqW6FAQ5YSbb2-ZJJL5",
text: "Phew, almost fell in it, thanks!"
photoUrls: ["https://...cdn url..."]
clientMutationId: "2"
}) {
commentEdge {
node {
id
text
documentUrls
photoUrls
timestamp
editedTimestamp
user {
edges {
node {
id
firstName
lastName
profilePictureUrl
}
}
}
}
}
issue {
comments {
count
}
}
}
}Mutations returns:
{
"data": {
"addComment": {
"commentEdge": {
"node": {
"id": "-KGADWP-bVxMtWqtgvI4-TFDD5EK",
"text": "Phew, almost fell in it, thanks!",
"documentUrls": null,
"photoUrls": [
"https://...cdn url..."
],
"timestamp": "2016-04-25T02:22:58.680Z",
"editedTimestamp": null,
"user": {
"edges": [
{
"node": {
"id": "-KGABu6mnxLorkH4Y0yw-kJ5I",
"firstName": "N",
"lastName": "S",
"profilePictureUrl": "https://scontent.xx.fbcdn.net/..."
}
}
]
}
}
},
"issue": {
"comments": {
"count": 2
}
}
}
}
}mutation Like {
like(input: {
issueId: "-KGABwWFqW6FAQ5YSbb2-ZJJL5",
clientMutationId: "3"
}) {
likeEdge {
node {
id
firstName
lastName
}
}
issue {
likes {
count
}
}
}
}Mutations returns:
{
"data": {
"like": {
"likeEdge": {
"node": {
"id": "-KGABu6mnxLorkH4Y0yw-kJ5I",
"firstName": "N",
"lastName": "S"
}
},
"issue": {
"likes": {
"count": 1
}
}
}
}
}mutation Unlike {
unlike(input: {
issueId: "-KGABwWFqW6FAQ5YSbb2-ZJJL5"
clientMutationId: "4"
}) {
deletedLikeUserId,
issue {
likes {
count
}
}
}
}Mutations returns:
{
"data": {
"unlike": {
"deletedLikeUserId": "-KGABu6mnxLorkH4Y0yw-kJ5I",
"issue": {
"likes": {
"count": null
}
}
}
}
}mutation DeleteComment {
deleteComment(input:{
id: "-KGADWP-bVxMtWqtgvI4-TFDD5EK"
clientMutationId:"5"
}) {
deletedCommentId
issue {
comments {
count
}
}
}
}Mutations returns:
{
"data": {
"deleteComment": {
"deletedCommentId": "-KGADWP-bVxMtWqtgvI4-TFDD5EK",
"issue": {
"comments": {
"count": 1
}
}
}
}
}mutation TagAgency{
tagAgency(input: {
issueId: "-KGABwWFqW6FAQ5YSbb2-ZJJL5"
agencyId: "-KGAHVAEHGSrU1Ho23FQ-R75E3P" ## ConEd ID
clientMutationId: "6"
}) {
agencyEdge {
node {
id
name
description
}
}
issue {
agencies {
count
}
}
}
}Mutations returns:
{
"data": {
"tagAgency": {
"agencyEdge": {
"node": {
"id": "-KGAHVAEHGSrU1Ho23FQ-R75E3P",
"name": "ConEdison",
"description": "New York City Power Utility"
}
},
"issue": {
"agencies": {
"count": 1
}
}
}
}
}query OpenIssues {
cityByName(internalName: "New York, NY") {
id
city: displayName
issues(
filterBy: { node: { status: { eq: OPEN }}}
orderBy: { node: { timestamp: DESCENDING }}
) {
edges {
node {
id
category
headline
description
location
photoUrl
timestamp
user {
edges {
node {
id
firstName
lastName
profilePictureUrl
}
}
}
likes {
count
}
comments {
edges {
node {
id
text
timestamp
user {
edges {
node {
id
firstName
lastName
profilePictureUrl
}
}
}
}
}
}
}
}
}
}
}
query IssueView($id: [ID!]!) {
issue(id: $id) {
id
category
headline
description
location
photoUrl
status
timestamp
closedTimestamp
user {
edges {
node {
id
firstName
lastName
profilePictureUrl
}
}
}
agencies {
edges {
node {
id
name
}
}
}
comments {
edges {
node {
id
text
timestamp
documentUrls
photoUrls
user {
edges {
node {
id
firstName
lastName
profilePictureUrl
}
}
}
}
}
}
likes {
count
}
}
}The query above expects id parameter:
{
"id": "-KGABwWFqW6FAQ5YSbb2-ZJJL5"
}This code is free software, licensed under the MIT license. See the LICENSE file for more details.