• Tutorials

    Here you'll find example queries or mutations for common tasks. You can run these in our API playground.

    Authentication is not covered here. For that, head to the authentication page.

    Get a user's identity

    This is quite straightforward - starting at the root node (query), fetch the user's pk, username and email fields.

    Query

    Token scopes required:

    • user:read
    query {
      user {
        pk
        username
        email
      }
    }
    
    Example response
    {
      "data": {
        "user": {
          "pk": 783350,
          "username": "joealcorn",
          "email": "joe.alcorn@marvelapp.com"
        }
      }
    }
    

    Get a user's projects

    Building on the query above, here we also ask for the user's first 2 projects. This touches on pagination (the pageInfo object). Read more about pagination, or see ProjectNode reference.

    Query

    Token scopes required:

    • user:read
    • projects:read
    query {
      user {
        pk
        username
        email
        projects(first: 2) {
          pageInfo {
            hasNextPage
            endCursor
          }
          edges {
            node {
              pk
              name
              isArchived
              createdAt
              prototypeUrl
            }
          }
        }
      }
    }
    
    Example response
    {
      "data": {
        "user": {
          "pk": 783350,
          "username": "joealcorn",
          "email": "joe.alcorn@marvelapp.com",
          "projects": {
            "pageInfo": {
              "hasNextPage": true,
              "endCursor": "YXJyYXljb25uZWN0aW9uOjE="
            },
            "edges": [
              {
                "node": {
                  "pk": 2691612,
                  "name": "Example",
                  "isArchived": false,
                  "createdAt": "2018-02-07T23:22:41+00:00",
                  "prototypeUrl": "https://marvelapp.com/bgfc3f4"
                }
              },
              {
                "node": {
                  "pk": 2650216,
                  "name": "Awesome project",
                  "isArchived": false,
                  "createdAt": "2018-01-25T12:43:29+00:00",
                  "prototypeUrl": "https://marvelapp.com/3he53fa"
                }
              }
            ]
          }
        }
      }
    }
    

    Embed a Marvel prototype

    When embedding a prototype, the first thing we need to do is generate the embed code. We can do this by asking for embed from the ProjectNode.

    We can then use the returned <iframe> code to embed the prototype in any HTML document.

    Query

    Token scopes required:

    • user:read
    • projects:read
    query {
      user {
        projects(first: 1) {
          edges {
            node {
              embed {
                url
                iframe
              }
            }
          }
        }
      }
    }
    
    Example response
    {
      "data": {
        "user": {
          "projects": {
            "edges": [
              {
                "node": {
                  "embed": {
                    "url":
                      "https://marvelapp.com/bgfc3f4?emb=1&iosapp=false&frameless=false",
                    "iframe":
                      "<iframe src=\"https://marvelapp.com/bgfc3f4?emb=1&iosapp=false&frameless=false\" width=\"452\" height=\"881\" allowTransparency=\"true\" frameborder=\"0\"></iframe>"
                  }
                }
              }
            ]
          }
        }
      }
    }
    

    Create a project

    So far all we've done is read data, but many use cases will also need to write (or mutate) data.

    We can do this with a mutation. In GraphQL, a mutation is simply a query that mutates data before returning data. They work very similar to regular queries, except instead of starting at the root query node, you start at mutation.

    A list of supported mutations can be found in the reference documentation, but here we're going to use createProject, which takes the CreateProjectInput type as an input.

    From the reference documentation, we can see the input type can take 5 arguments:

    • name, which is the only required argument (denoted by the !)
    • password, which is a pro feature requiring users to supply a password before viewing the prototype
    • teamPk, which will give a team and all its users access to the project
    • companyPk, which will add a company to a project, taking advantage of Marvel's company collaboration features
    • settings, which is a ProjectSettingsInput type and has its own arguments

    The return type has two fields: ok, a boolean, and project, which is a ProjectNode. Just like the queries we've seen previously, we specify which fields to return in our mutation.

    Query

    Token scopes required:

    • user:read
    • projects:read
    • projects:write
    mutation {
      createProject(input: {name: "My example project", settings: {deviceFrame: IPHONEX}}) {
        ok
        project {
          pk
          name
          prototypeUrl
          settings {
            deviceFrame
          }
        }
      }
    }
    
    Example response
    {
      "data": {
        "createProject": {
          "ok": true,
          "project": {
            "pk": 3052913,
            "name": "My example project",
            "prototypeUrl": "https://marvelapp.com/8j0gjb4",
            "settings": {
              "deviceFrame": "IPHONEX"
            }
          }
        }
    }
    

    Get a project's screens

    Token scopes required:

    • user:read
    • projects:read

    Getting the screens for a project is fairly straightforward.

    From a ProjectNode, which we're selecting here by its pk, we can ask for the screens field, which will return a paginated connection:

    query {
      project(pk: 1) {
        name
        screens(first: 2) {
          edges {
            node {
              name
              modifiedAt
            }
          }
        }
      }
    }
    

    That would return something along these lines:

    {
      "data": {
        "project": {
          "name": "My project",
          "screens": {
            "edges": [
              {
                "node": {
                  "name": "Onboarding-location.png",
                  "modifiedAt": "2018-02-15T11:52:27.974392+00:00"
                }
              },
              {
                "node": {
                  "name": "Onboarding-chat.png",
                  "modifiedAt": "2018-02-15T11:52:28.226316+00:00"
                }
              }
            ]
          }
        }
      }
    }
    

    This works well, but as you can see we're just selecting screen metadata here and not the screen content itself.

    Getting the screen's associated content is a little more complicated, because it is a union type, which effectively means the content could be one of many types. Currently Marvel only supports ImageScreen, but you can see an up to date list of the types included in the union in the ScreenTypeUnion reference.

    To query on a union type, we need to use a GraphQL construct called a fragment, of which there are two types:

    Our example will be using regular fragments for illustration purposes, but inline fragments work as well. The slight increase in complexity of requiring fragments allows us to support more screen types in future without breaking existing queries.

    Let's define a fragment that will query for the fields we're interested in on an ImageScreen:

    fragment image on ImageScreen {
      filename
      url
      height
      width
    }
    

    Now let's add that to the query we looked at previously, and use it to query for the content field of the ScreenNode. Additionally, we'll ask the server to return a special __typename field, to let us know which of the union's supported types has been returned:

    fragment image on ImageScreen {
      filename
      url
      height
      width
    }
    
    query {
      project(pk: 1) {
        name
        screens(first: 2) {
          edges {
            node {
              name
              modifiedAt
              content {
                __typename
                ...image
              }
            }
          }
        }
      }
    }
    

    What we've effectively done is told the server to return us these fields only if the type matches ImageScreen. Our response would look something like this:

    {
      "data": {
        "project": {
          "name": "My project",
          "screens": {
            "edges": [
              {
                "node": {
                  "name": "Onboarding-location.png",
                  "modifiedAt": "2018-02-15T11:52:27.974392+00:00",
                  "content": {
                    "__typename": "ImageScreen",
                    "filename": "Onboarding-location.png",
                    "url":
                      "http://127.0.0.1:8080/static/assets/images/onboarding/iphone6/Onboarding-location.png",
                    "height": 1334,
                    "width": 750
                  }
                }
              },
              {
                "node": {
                  "name": "Onboarding-chat.png",
                  "modifiedAt": "2018-02-15T11:52:28.226316+00:00",
                  "content": {
                    "__typename": "ImageScreen",
                    "filename": "Onboarding-chat.png",
                    "url":
                      "http://127.0.0.1:8080/static/assets/images/onboarding/iphone6/Onboarding-chat.png",
                    "height": 1334,
                    "width": 750
                  }
                }
              }
            ]
          }
        }
      }
    }
    

    Add designs to a project

    Token scopes required:

    • projects:write

    Getting designs into Marvel is a two step process. First, you need to create a screen within a project to hold your image, and then you need to update the screen with the image itself, which is done by posting a file to a second endpoint. Let’s walk through the process.

    Step 1: Create a new screen

    The first thing we need is a screen to which our image will belong. (If you're updating a pre-existing screen, skip straight to step 2.

    In order to create a screen, we need to use the aptly-named createScreen mutation. Let’s write our mutation.

    In the query below, we call the createScreen mutation, passing it the pk of the project that this screen will belong to and a name for the screen. Crucially, we ask the server to return the screen’s uploadUrl, which we’ll need to use in step two.

    Tip: You can use aliases to create many screens with a single call

    Query:

    mutation {
      createScreen(input: { projectPk: 2778196, name: "Tutorial screen" }) {
        ok
        screen {
          pk
          uploadUrl
          content {
            ... on ImageScreen {
              url
            }
          }
        }
      }
    }
    

    And the response:

    {
      "data": {
        "createScreen": {
          "ok": true,
          "screen": {
            "pk": 39574307,
            "uploadUrl": "http://127.0.0.1:8080/api/ingest/image/39574307/",
            "content": null
          }
        }
      }
    }
    

    In our response we can see that the screen was created successfully, but without any content. We've now got our uploadUrl, so we can move on to the second step to fix that.

    Step 2: Upload your image

    Now we want to upload the actual image file, and to do that we have to POST to the screen's uploadUrl that we retrieved in step 1, and include the same Authorization header we used to authenticate our GraphQL query.

    Here's an example using using curl in a shell, but a HTTP client in any language should be able to do this.

    curl \
        -H "Authorization: Bearer <token>" \
        -X POST \
        -F "file=@path/to/image.png" \
        https://marvelapp.com/api/ingest/image/38623843/
    

    See uploads for more information.

    Retrieve or create comments

    Coming soon! Keep an eye on #changelog for updates!