├── .gitignore
├── LICENSE
├── README.md
├── docs
├── about.md
├── aggregations
│ ├── advanced.md
│ └── basics.md
├── app-management
│ ├── admin-tokens.md
│ ├── alias.md
│ ├── analytics.md
│ ├── export-schema.md
│ ├── fork-app.md
│ ├── multi-region.md
│ ├── teams.md
│ └── token-expiration.md
├── authentication
│ ├── change-password.md
│ ├── permissions.md
│ ├── social.md
│ ├── super-users.md
│ └── tokens.md
├── changelog.md
├── community.md
├── coredata
│ ├── connections-pagination.md
│ ├── index.md
│ ├── mutations.md
│ ├── queries.md
│ ├── schema.md
│ └── subscriptions.md
├── css
│ ├── base.css
│ └── styles.css
├── custom-logic
│ ├── async-operations.md
│ ├── composition.md
│ ├── index.md
│ ├── logs.md
│ ├── operation-phase.md
│ ├── post-operations.md
│ └── pre-operations.md
├── errors.md
├── faq.md
├── files
│ ├── index.md
│ ├── querying.md
│ └── uploading.md
├── getting-started
│ ├── community.md
│ └── quickstart-video.md
├── images
│ ├── authentication
│ │ ├── Add_Permissions.png
│ │ └── Relation_Permission.png
│ ├── coredata
│ │ ├── Add_Field.png
│ │ ├── Add_Type.png
│ │ ├── Join_Table.png
│ │ ├── Many_To_Many.png
│ │ ├── One_To_Many.png
│ │ └── One_To_One.png
│ ├── favicon.ico
│ ├── home.png
│ ├── integrations
│ │ ├── Add_Restaurant.png
│ │ ├── Algolia_Index.png
│ │ ├── Algolia_Viewer.png
│ │ ├── Configure_Auth0.png
│ │ ├── Configure_Auth0_Account.png
│ │ ├── Digits_Oauth_Echo_Headers.png
│ │ ├── Geo_Query.png
│ │ ├── Get_Access_Token.png
│ │ ├── Insert_Jwt_Header.png
│ │ ├── Link_Google_User.png
│ │ ├── Login_User_With_Digits.png
│ │ ├── Restaurant_Geo.png
│ │ └── Restaurant_Searchable.png
│ ├── management
│ │ ├── Add_Teammate_Panel.png
│ │ ├── Add_Teammate_Settings.png
│ │ ├── Multi_Region.png
│ │ └── Set_Alias.png
│ └── quickstart
│ │ ├── GraphiQL.htm
│ │ ├── PQ Check Icon.png
│ │ ├── android.png
│ │ ├── angular2.svg
│ │ ├── cerebral.png
│ │ ├── ios.png
│ │ ├── reactnative.svg
│ │ └── vue2.png
├── index.md
├── index2.md
├── integrations
│ ├── email.md
│ ├── index.md
│ ├── monitoring.md
│ ├── passwordless.md
│ ├── payments.md
│ ├── push.md
│ ├── search.md
│ └── social
│ │ ├── login-with-auth0.md
│ │ ├── login-with-native.md
│ │ └── setup.md
├── persisted-queries
│ ├── getting-started.md
│ ├── index.md
│ └── querying-from-client.md
├── realtime
│ ├── slackr-chat-app.md
│ ├── subscriptions-apollo.md
│ └── subscriptions-react.md
├── search
│ ├── fulltext-search.md
│ └── geo-search.md
├── starter-kits.md
└── tutorials
│ ├── adding-social-auth.md
│ ├── advanced-queries.md
│ ├── aggregations-in-graphql.md
│ ├── android-push-notifications.md
│ ├── apollo-optics-graphql-server.md
│ ├── authentication-in-graphql.md
│ ├── ios-push-notifications.md
│ ├── migrate-mongodb-graphql.md
│ ├── persisted-queries.md
│ ├── realtime-apps-with-subscriptions.md
│ ├── search-with-algolia.md
│ └── sending-push-notifications.md
├── mkdocs.yml
└── theme
├── main.html
└── partials
└── toc.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 | local_settings.py
55 |
56 | # Flask stuff:
57 | instance/
58 | .webassets-cache
59 |
60 | # Scrapy stuff:
61 | .scrapy
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # PyBuilder
67 | target/
68 |
69 | # IPython Notebook
70 | .ipynb_checkpoints
71 |
72 | # pyenv
73 | .python-version
74 |
75 | # celery beat schedule file
76 | celerybeat-schedule
77 |
78 | # dotenv
79 | .env
80 |
81 | # virtualenv
82 | venv/
83 | ENV/
84 |
85 | # Spyder project settings
86 | .spyderproject
87 |
88 | # Rope project settings
89 | .ropeproject
90 | site
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Scaphold
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # scaphold-docs
2 | Documentation for Scaphold.io containing tons of helpful resources for people new to GraphQL!
3 |
4 | You can find the [beautiful GraphQL documentation here!](https://docs.scaphold.io)
5 |
--------------------------------------------------------------------------------
/docs/about.md:
--------------------------------------------------------------------------------
1 | # About
--------------------------------------------------------------------------------
/docs/aggregations/advanced.md:
--------------------------------------------------------------------------------
1 | In the previous section, we showed how you can crunch the numbers for all the records
2 | in your dataset. This is useful for administrative tasks, but often our applications
3 | are more user-centric. The good news is that aggregations work for every connection in
4 | your API.
5 |
6 | Let's compound on the previous example and ask the question, "How many reads did MY posts
7 | get on average in 2016?". To make this work, we need to introduce a `User` type.
8 |
9 | Once we setup the connection between our `User` and `Article` types, we can issue a query like this
10 |
11 | This query will only perform the aggregation on the articles that are attached to the logged in
12 | user via the `articles` connection. You can continue to manipulate these queries with more and more
13 | advanced filters and aggregations. You'll find that every connection in your API operates the same
14 | way allowing you do run multiple aggregations at different levels in your GraphQL API!
15 |
16 | Here is a simple connection between `User` and `Article`:
17 |
18 | ```graphql
19 | type User {
20 | id: ID!
21 | username: String
22 | password: Secret
23 | articles: ArticleConnection
24 | }
25 |
26 | type Article {
27 | id: ID!
28 | title: String
29 | content: Text
30 | recommends: Int
31 | reads: Int
32 | author: User
33 | }
34 | ```
35 |
36 | We want to know how many reads did I get in 2016? Conversion?
37 |
38 | ```graphql
39 | query PostData {
40 | viewer {
41 | user {
42 | articles(where:{ createdAt: { gt: "2016", lt: "2017" }}) {
43 | aggregations {
44 | count
45 | sum {
46 | recommends
47 | reads
48 | }
49 | avg {
50 | recommends
51 | reads
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 | ```
59 |
60 | With Scaphold you can query extremely complex aggregations and combine that with the power of GraphQL connections. This gives incredible power to the client that never before existed. Now go try it in your apps!
--------------------------------------------------------------------------------
/docs/aggregations/basics.md:
--------------------------------------------------------------------------------
1 | Imagine you were building a blogging platform like Medium. It would be an obvious value add
2 | to be able to slice & dice your data to better understand how your posts are performing. Let's
3 | say our blogging platform has an `Article` model like this:
4 |
5 | A single `Article` type:
6 |
7 | ```graphql
8 | type Article {
9 | id: ID!
10 | title: String
11 | content: Text # Like a String but larger
12 | recommends: Int
13 | reads: Int
14 | }
15 | ```
16 |
17 | Now, we want to know how many times were all the articles reads in 2016?
18 |
19 | ```graphql
20 | query LikeData {
21 | viewer {
22 | allArticles(where:{ createdAt: { gt: "2016", lt: "2017" }}) {
23 | aggregations {
24 | sum {
25 | reads
26 | }
27 | }
28 | }
29 | }
30 | }
31 | ```
32 |
33 | With aggregations and GraphQL this is easy!
34 |
35 | Digging deeper. What is the conversion from reads to recommends?
36 |
37 | ```graphql
38 | query PostData {
39 | viewer {
40 | allArticles(where:{ createdAt: { gt: "2016", lt: "2017" }}) {
41 | aggregations {
42 | count
43 | sum {
44 | recommends
45 | reads
46 | }
47 | avg {
48 | recommends
49 | reads
50 | }
51 | }
52 | }
53 | }
54 | }
55 | ```
56 |
57 | This is great, but what if we also wanted to know the average read count and the total number
58 | of articles listed on the site.
59 | While we're at it, lets also grab the total & average number of recommendations for our articles.
60 |
61 | This query is both intuitive to understand and performant. Behind the scenes all of these
62 | metrics are calculated in a single query keeping your api snappy and you user's happy! Aggregations
63 | open the door for all kinds of insights into our data. For example, we could use the query above
64 | to answer a question like what is the average conversion rate from a read to a recommendation.
65 |
66 | This is just touching the surface of our aggregation capabilities.
--------------------------------------------------------------------------------
/docs/app-management/admin-tokens.md:
--------------------------------------------------------------------------------
1 | # Admin Tokens
2 |
3 | With admin tokens, you have the ability to bypass any permissions that have been set in your API. By setting this as `Authorization: Bearer {ADMIN_TOKEN}` in your header
4 | like any other auth token in Scaphold, you become an admin user. This is useful for importing or exporting data, any one-off data management tasks that need to be done, or scheduled jobs.
5 |
6 | To create a admin token, navigate to **Settings Page > Admin Tokens > Create**
7 |
8 | !!! note
9 |
10 | Admin tokens never expire, unless you delete them.
--------------------------------------------------------------------------------
/docs/app-management/alias.md:
--------------------------------------------------------------------------------
1 | # Aliases
2 |
3 | Simplify your API URL with an alias!
4 |
5 | Now when you create an app, you can choose to set an alias. An alias is a unique name for your application that can be used in the URL to access your API.
6 | For example, if I set an alias for my app of `scaphold` then I could access my application at the url `https://us-west-2.api.scaphold.io/graphql/scaphold`.
7 |
8 |
11 |
12 | You can also change an alias at any time by clicking `My API` at the top of the page.
13 |
14 |
--------------------------------------------------------------------------------
/docs/app-management/analytics.md:
--------------------------------------------------------------------------------
1 | # Analytics
2 |
3 | Scaphold comes full circle with a comprehensive analytics dashboard so you can always get the latest updates on the performance, growth, and usage of your app in production.
4 |
5 | Name | Description
6 | -------------- | --------------
7 | Request Counts | Total number of requests broken down by hour
8 | Average Response Time | Measures latency (in seconds) in your API broken down by hour
9 | User Growth | Tracks the number of new users that are signing up for your app
10 | Data Throughput | Amount of data (in MB) that's being transmitted over the wire to your client apps
11 | Resolvers by Type | Sorts your GraphQL requests by the associated type to help break down which part of your API is being used the most
12 | Error Counts | Total number of errors broken down by hour
13 | Application Logs | Table of all the latest logs for your API
14 |
15 | Our goal is to give you as much transparency as we can into your GraphQL API, so you can make the best app possible. Please email us at [support@scaphold.io](mailto:support@scaphold.io)
16 | if you have any requests for metrics you would like to see that would help you better measure and analyze the success of your app.
--------------------------------------------------------------------------------
/docs/app-management/export-schema.md:
--------------------------------------------------------------------------------
1 | # Export Schema
2 |
3 | Exporting your schema can be useful if you want to see what the raw JSON version of your data model looks like.
4 |
5 | Scaphold manages two versions of your schema:
6 |
7 | 1. **Standard GraphQL**: Download a standard version of your GraphQL schema that you can use with any GraphQL server.
8 |
9 | 2. **Scaphold Schema**: Save a copy of your up-to-date GraphQL schema that you can use to create an app on Scaphold.
--------------------------------------------------------------------------------
/docs/app-management/fork-app.md:
--------------------------------------------------------------------------------
1 | # Fork App
2 |
3 | Forking an app will make an exact copy of the app in a new environment. That means the new app will automatically configure itself to be identical to the
4 | source but it will have its own data, schema, and API. This is useful for testing schema changes before making any migrations to your production applications.
5 | Once you have tested your changes and manually merged them into your production app you can safely remove the fork.
6 |
7 | You can fork an app in 2 ways:
8 |
9 | 1. **Apps Page > Underneath each API link** for each app panel (top-right)
10 |
11 | 2. **Settings Page > Advanced**
--------------------------------------------------------------------------------
/docs/app-management/multi-region.md:
--------------------------------------------------------------------------------
1 | # Regions
2 |
3 | Scaphold is now deployed in multiple regions and counting! With growing demand for Scaphold's service by developers around the world, we're always open to setting up new infrastructure
4 | so that your data lives closer to you.
5 |
6 | Currently, we're deployed in...
7 |
8 | 1. US West (Oregon)
9 |
10 | 2. EU West (Ireland)
11 |
12 | 3. More to come. If you wish to have Scaphold deployed on a data center in your region, please contact us at [support@scaphold.io](mailto:support@scaphold.io).
13 |
14 | To switch between regions, select the region you wish to use in the **header bar of the Scaphold dashboard**.
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/app-management/teams.md:
--------------------------------------------------------------------------------
1 | # Teams
2 |
3 | App development is always more fun and faster in teams! Which is why we've provided you the ability to collaborate with others on apps.
4 |
5 | There are two ways to add a teammate:
6 |
7 | 1. **Bottom left of the left side panel** in the app-view.
8 |
9 |
10 |
11 | 2. **Settings Page > Team**
12 |
13 |
14 |
15 | In addition to adding a teammate, you may also want to remove a teammate. To do so, navigate to the **Settings Page > Team**, click on the bubble under **Teammates**
16 | that designates the user you wish to remove from this app, and a dropdown will appear that will allow you to remove that user.
17 |
18 |
--------------------------------------------------------------------------------
/docs/app-management/token-expiration.md:
--------------------------------------------------------------------------------
1 | # Token Expiration
2 |
3 | Scaphold gives you full control over how your tokens are managed for authentication. As such, we provide the ability to control the expiration of the JsonWebToken (JWT) measured in seconds
4 | from when it was issued. This refers to the token issued during mutation calls such as `loginUser`, or any social auth flow.
5 |
6 | To configure the token expiration time, please navigate to **Settings Page > Advanced**
7 |
8 | !!! note
9 |
10 | The default value is 1,296,000 seconds (i.e. 15 days, or ~2 weeks).
--------------------------------------------------------------------------------
/docs/authentication/change-password.md:
--------------------------------------------------------------------------------
1 | # Change Password
2 |
3 | There are two ways to change a user's password:
4 |
5 | 1. **Reset password**
6 |
7 | Use the `updateUser` mutation to update a user's password given a user's `id`, and new `password`. Note here that no server-side validation for the old password.
8 |
9 | 2. **Forgot password**
10 |
11 | Use the `changeUserPassword` mutation to update a user's password given a user's `id`, `oldPassword`, and `newPassword`.
--------------------------------------------------------------------------------
/docs/authentication/permissions.md:
--------------------------------------------------------------------------------
1 | ## Permissions (Authorization)
2 |
3 | !!! note "Important"
4 |
5 | If a type has no permissions then anyone can perform any operations on it so make sure you add them before launching!
6 |
7 | Scaphold implements a permissions system that allows you to define powerful access control rules by leveraging a combination of features from role-based access control systems (RBAC) as well as the connections in your API's graph to define what users can access what information.
8 |
9 | Each type and field in your schema has an optional set of permissions applied to it. When a user tries to complete an operation, your API checks the type- and field-level permissions and validates that the user is authorized to complete that operation. When you login to the Scaphold portal, you are logged in as an admin user and will have complete access to your application.
10 |
11 | To add a permission, click on the `+` sign in the top right for the panel of the type you wish to add a permission to. There you can add a permission for all of the fields of a particular type or only specific fields on that type.
12 |
13 |
14 |
15 | The following sections delineate the various types of permissioning that you can apply to your data.
16 |
17 |
20 |
21 | ### Everyone
22 |
23 | `Everyone` scoped permissions are the loosest available. Everyone can access the type. No login required.
24 |
25 | ### Authenticated
26 |
27 | `Authenticated` scoped permissions required a valid auth token to be present in the request headers (See [authentication](#token-auth) for more details). Scaphold provides a number of authentication mechanisms that all work seamlessly with your permissions.
28 |
29 | ### Roles
30 |
31 | GraphQL types used in role-based permissioning to manage roles, members, and access levels.
32 |
33 | ```graphql
34 | type User {
35 | id: ID!
36 | username: String
37 | password: Secret
38 | credentials: CredentialConnection
39 | lastLogin: DateTime
40 | roles: RoleConnection
41 | createdAt: DateTime
42 | modifiedAt: DateTime
43 | }
44 |
45 | type Role {
46 | id: ID
47 | name: String
48 | members: UserConnection
49 | createdAt: DateTime
50 | modifiedAt: DateTime
51 | }
52 |
53 | type UserRoles {
54 | accessLevel: AccessLevel
55 | createdAt: DateTime
56 | modifiedAt: DateTime
57 | }
58 |
59 | enum AccessLevel {
60 | admin
61 | readwrite
62 | readonly
63 | }
64 | ```
65 |
66 | `Role` permissions allow you to layer more generic role-based authentication methods on top of the connection and user-based permissions you already have. We have added a few queries and mutations that make it easy to manage your new roles and permissions.
67 |
68 | In particular, you role-based permissioning is concerned with 3 object types: `User`, `Role`, and a through type called `UserRoles`. In addition, we use the enum `AcessLevel` to manage how much access we should provide a particular instance of a `UserRole`.
69 |
70 | Name | Inputs | Description
71 | -------------- | -------------- | --------------
72 | getRole | `id: ID!` | Get objects of type Role by id.
73 | createRole | `name: String!` | Creates a new role and enrolls the creator as a role admin.
74 | updateRole | `id: ID!`, `name: String` | Update an existing role.
75 | deleteRole | `id: ID!` | Delete an existing role. Only role admins can delete roles.
76 | addToUserRolesConnection | `userId: ID!`, `roleID: ID!`, `accessLevel: AccessLevel` | Enrolls a user as a member of a role. Only admins can create other admins and you must be an admin to enroll another user.
77 | updateUserRolesConnection | `userId: ID!`, `roleID: ID!`, `accessLevel: AccessLevel` | Updates a connection between an object of type `User` and an object of type `Role`.
78 | removeFromUserRolesConnection | `userId: ID!`, `roleId: ID!` | Removes a user from a role. Anyone can disenroll themself and admins can disenroll anyone.
79 |
80 | By default, we create a special `admin` role for each of your apps. Users that are enrolled into the `admin` role are given full access to your GraphQL API without having to specify any custom permissions.
81 |
82 | !!! note
83 |
84 | `Role` scoped permissions can be used alongside existing `Relation` scoped permissions to easily create complex access control rules. Let's take an example where we would like to have a **set of notes** that only executives of my company can see.
85 | Part of our schema might look something like this:
86 |
87 | ```graphql
88 | // Schema
89 | type User {
90 | id: ID!
91 | username: String!
92 | authoredExecNotes: ExecutiveNoteConnection
93 | }
94 |
95 | type ExecutiveNote {
96 | id: ID!
97 | author: User
98 | content: String
99 | }
100 |
101 | // Permissions
102 | [{
103 | scope: "ROLE",
104 | roles: ["Executives"],
105 | read: true,
106 | create: true
107 | }
108 | {
109 | scope: "RELATION",
110 | userFields: ["author"],
111 | update: true,
112 | delete: true
113 | }]
114 | ```
115 |
116 | ### Relation
117 |
118 | Example of `Relation` permission
119 |
120 | !!! quote ""
121 |
122 | In Slack, a user should only be able to view messages that belong to a channel that you're a member of. Looking at the example to the right, if you wanted to add this permission, you would add a `Relation` scoped permission to the `Message` type with the user fields `channel.members`.
123 |
124 |
125 |
126 | ```graphql
127 | # Simple Slack schema
128 |
129 | type Channel {
130 | id: ID!
131 | messages: [Message]
132 | members: [User]
133 | }
134 |
135 | type Message {
136 | id: ID!
137 | body: String!
138 | channel: Channel
139 | }
140 | ```
141 |
142 | `Relation` scoped permissions use the connections in your data's graph to authorize behaviors. When you add a `Relation` permission, you can specify a path in your data graph that from the type you're adding the permission
143 | to back to the authenticated user.
144 |
145 | In our example, you would have to use the query like this:
146 |
147 | ```graphql
148 | {
149 | viewer {
150 | user {
151 | channels {
152 | messages {
153 | body
154 | }
155 | }
156 | }
157 | }
158 | }
159 | ```
160 |
161 | !!! warning ""
162 |
163 | Querying through `viewer.allMessages` would throw a permissioning error.
164 |
165 | In order to keep queries that are protected by `Relation` scoped permissions performant, you must query through `viewer.user`. This allows us to trace the user field's path to ensure that you're only accessing objects
166 | related to the logged in user.
--------------------------------------------------------------------------------
/docs/authentication/social.md:
--------------------------------------------------------------------------------
1 | ## Social
2 |
3 | For social authentication, Scaphold has a solution for that as well! Integrating with OAuth providers like Facebook, Google, and Twitter has never been easier with Scaphold's Auth0 integration.
4 |
5 | Find out how to [integrate social authentication](/integrations/social/setup/) or [integrate passwordless authentication](/integrations/passwordless/).
6 |
--------------------------------------------------------------------------------
/docs/authentication/super-users.md:
--------------------------------------------------------------------------------
1 | ## Super Users
2 |
3 | Scaphold provides the ability for you to generate an admin token that gives you super user access that supercedes all permission rules and access controls.
4 | This is a great tool for importing or exporting data, any one-off data management tasks that need to be done, or scheduled jobs.
5 |
6 | Please refer to the [Admin Token section](/app-management/admin-tokens/) under App Management for more details.
--------------------------------------------------------------------------------
/docs/authentication/tokens.md:
--------------------------------------------------------------------------------
1 | Scaphold seamlessly handles user authentication for you. Each Scaphold application comes with a default User model which includes a `username` and `password` that are used to authenticate your users.
2 | We securely encrypt and store each user's password as well as ensure that they are not readable.
3 |
4 | ## Token
5 |
6 | Logging in a user is simple. Use the `loginUser` mutation we provide you and we will return a JSON Web Token (JWT) if the credentials match. To authenticate a user, you simply set the `Authorization` HTTP header of your request with the format `Bearer {TOKEN_FROM_LOGIN_USER}`.
7 |
8 | This token informs your API what user is logged in at any given time and enables our permissions system to layer access control rules on your data.
9 |
10 | !!! tip ""
11 |
12 | Example `loginUser` query
13 |
14 | ```shell
15 | curl -X POST https://us-west-2.api.scaphold.io/graphql/scaphold-graphql \
16 | -H "Content-Type: application/json" \
17 | -d '{"query": "mutation LoginUserQuery ($input: LoginUserInput!) { loginUser(input: $input) { token user { id username createdAt } } }",
18 | "variables": { "input": { "username": "elon@tesla.com", "password": "SuperSecretPassword" } } }'
19 | ```
20 |
21 | ```javascript
22 | import request from 'request';
23 |
24 | const data = {
25 | "query": `mutation LoginUserQuery ($input: LoginUserInput!) {
26 | loginUser(input: $input) {
27 | token
28 | user {
29 | id
30 | username
31 | createdAt
32 | }
33 | }
34 | }`,
35 | "variables": {
36 | "input": {
37 | "username": "elon@tesla.com",
38 | "password": "SuperSecretPassword"
39 | }
40 | }
41 | };
42 |
43 | request({
44 | url: "https://us-west-2.api.scaphold.io/graphql/scaphold-graphql",
45 | method: "POST",
46 | json: true,
47 | headers: {
48 | "content-type": "application/json",
49 | },
50 | body: data
51 | }, (error, response, body) => {
52 | if (!error && response.statusCode == 200) {
53 | console.log(JSON.stringify(body, null, 2));
54 | } else {
55 | console.log(error);
56 | console.log(response.statusCode);
57 | }
58 | });
59 | ```
60 |
61 | The above command returns an object structured like this:
62 |
63 | ```json
64 | {
65 | "data": {
66 | "loginUser": {
67 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODI4ODI0ODgsImlhdCI6MTQ4MTU4NjQ4OCwiYXVkIjoiNDRiZTA4NmYtYmYzMy00OTk3LTgxMzYtOWMwMWQ5OWE4OGM0IiwiaXNzIjoiaHR0cHM6Ly9zY2FwaG9sZC5pbyIsInN1YiI6IjcifQ.TDRtD5vD7MIVrViDgVMThhzOzE_teufTo51a4GZ3aGA",
68 | "user": {
69 | "id": "VXNlcjo3",
70 | "username": "elon@tesla.com",
71 | "createdAt": "2016-12-08T20:43:14.000Z"
72 | }
73 | }
74 | }
75 | }
76 | ```
77 |
78 | !!! note "**Important**: Use the `token` in the response in the header of future requests as:"
79 |
80 | `Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODI4ODI0ODgsImlhdCI6MTQ4MTU4NjQ4OCwiYXVkIjoiNDRiZTA4NmYtYmYzMy00OTk3LTgxMzYtOWMwMWQ5OWE4OGM0IiwiaXNzIjoiaHR0cHM6Ly9zY2FwaG9sZC5pbyIsInN1YiI6IjcifQ.TDRtD5vD7MIVrViDgVMThhzOzE_teufTo51a4GZ3aGA`
81 |
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## May 7, 2017
4 |
5 | - Added ability to add connections in the "Add Type" and "Edit Type" side bars
6 |
7 | - Bug fixes:
8 | - Enums now allow single character values
9 | - Fix authentication error in Scaphold admin portal for expired or malformed tokens
10 |
11 | ## April 22, 2017
12 |
13 | - Added **new** [Hobby pricing tier](https://scaphold.io/pricing)
14 |
15 | - Algolia `query` input field is no longer mandatory
16 |
17 | - Bug fixes:
18 | - Fixed font-face for non-Mac environments
19 |
20 | ## April 12, 2017
21 |
22 | - Added [Algolia geo-search query parameters](/integrations/search/#geo-search)
23 |
24 | - Updated docs and layout
25 |
26 | ## April 3, 2017
27 |
28 | - Check expired and/or invalid token if `Authorization` header is provided
29 |
30 | - Authorization errors for invalid tokens now throw `401` error status codes instead of `403`
31 |
32 | - `401` errors are thrown if auth token is malformed now
33 |
34 | - Bug fixes:
35 | - Top-level error handling should not throw stack trace
36 |
37 | ## March 21, 2017
38 |
39 | - Upgraded subscriptions package to Apollo 1.0
40 |
41 | ## March 10, 2017
42 |
43 | - Added [AND & OR operators](/coredata/queries/#querying-with-and-or)
44 |
45 | - Allow enum connections
46 |
47 | - Bug fixes:
48 | - Algolia delete indexes after integration is deleted
49 | - Stripe integration via custom logic
50 | - Role scoped permissions update for admin user
51 |
52 | ## February 22, 2017
53 |
54 | - List of date errors
55 |
56 | - Bug fixes:
57 | - Updates with logic functions causing problems on return values.
58 |
59 | ## February 17, 2017
60 |
61 | - Added [persisted queries](/persisted-queries/)
62 |
63 | - Added `JSON` type for fields
64 |
65 | - Bug fixes:
66 | - Fix for iOS and Android push notifications
67 | - Return unmasked Stripe IDs
68 | - Apollo Optics bug to stop background tasks
69 |
70 | ## February 9, 2017
71 |
72 | - Deeply nested mutation permissions: you can now use deep `userFields` to authorize permissions for mutations multple models away.
73 |
74 | - `node` on `viewer` now returns app ID
75 |
76 | ## February 1, 2017
77 |
78 | - `self` option for permissions
79 |
80 | - Formatting for type descriptions
81 |
82 | - Custom payment plans now an option
83 |
84 | ## January 23, 2017
85 |
86 | - UTF8 databases
87 |
88 | - Bug fixes:
89 | - Fix for `_typename` in aggregations.
90 |
91 | ## January 17, 2017
92 |
93 | - Added [aggregations on connections](/aggregations/basics/)
94 |
95 | ## January 9, 2017
96 |
97 | - [Scaphold Community Page](https://scaphold.io/community/)! Find all the resources you need to launch a production app with GraphQL.
98 |
99 | - Bug fixes:
100 | - Subscriptions filter with ID now accepts opaque unique ID properly
101 |
102 | ## January 6, 2017
103 |
104 | - [Custom Logic](#custom-logic): Use Scaphold's powerful webhook system to define custom business logic for your apps.
105 |
106 | - Added enums as values to `orderBy` arguments on connections
107 |
108 | ## December 28, 2016
109 |
110 | - Extended ordering functionality on a connection to order by an enum field
111 |
112 | ## December 26, 2016
113 |
114 | - [New landing page](https://scaphold.io)!
115 |
116 | - [User authentication](#token):
117 | - Reset password
118 | - Forgot password
119 |
120 | - Better error handling:
121 | - JSON syntax errors in Query Variables (no need to input empty object literal anymore)
122 | - Accessing apps that don't exist or belong to you won't hang on loading page anymore
123 |
124 | - Updated [referral email](https://scaphold.io/referral)!
125 |
126 | - DateTime input form in Explorer page
127 |
128 | ## December 7, 2016
129 |
130 | - [Multiple Regions](#regions): We're launched to two regions now - US West (Oregon) and EU West (Ireland).
131 | Please message us if you have a need for a new data center in your region!
132 |
133 | - [Super Tokens](#super-tokens): Create a new super token with a click of a button. Use this for administrative tasks.
134 |
135 | - [Token Expiration](#token-expiration): Configure the expiration time of your JWT tokens for your users.
136 |
--------------------------------------------------------------------------------
/docs/community.md:
--------------------------------------------------------------------------------
1 | # [Community](https://scaphold.io/community/)
2 |
3 | ## [Slack](http://slack.scaphold.io)
4 |
5 | Join the Scaphold community Slack channel! This is where we all hang out :tada:
6 |
7 | ## [Featured](https://scaphold.io/community/projects/)
8 |
9 | Scaphold has featured projects that have been built from our community of amazing developers.
10 |
11 | ## [Learn](https://scaphold.io/community/learn/)
12 |
13 | Learn from a spectacular list of resources to get you ramped up on GraphQL, Raect, Apollo, and more. We aggregate articles from across entire
14 | GraphQL community to help you find the right learning experience for you.
15 |
16 | ## [Projects](https://scaphold.io/community/projects/)
17 |
18 | The Scaphold community builds new projects & starter kits all the time. Check them out on our
19 | community page! If you have a project you would like to feature let us know on our [Slack Community Channel](http://slack.scaphold.io).
20 |
21 | ## [Blog](https://scaphold.io/community/blog/)
22 |
23 | Follow our blog for the latest news on GraphQL, Scaphold, and more. We'll write about best practices, how to pair GraphQL with popular technologies that you might use already, and even what we're up to ourselves.
24 |
25 | ## [FAQ](https://scaphold.io/community/questions/)
26 |
27 | Have a question? It's probably been answered before! Make your way to Scaphold's FAQ to get quick answers on frequently asked questions.
--------------------------------------------------------------------------------
/docs/coredata/index.md:
--------------------------------------------------------------------------------
1 | # Core Data
2 |
3 | Scaphold's core data platform allows you to easily define complex data models that are instantly deployed
4 | to a production GraphQL API backed by a highly available SQL cluster. Core Data provides a great set of
5 | tools for powering core application logic.
6 |
7 | Every type in your schema that implements the `Node` interface is backed by core data. That means every
8 | type that implements `Node` maps to a single table and can be related to any other core data types via
9 | `Connection` fields. Each core data type `X` also receives a `createX`, `updateX`, and `deleteX` mutation as well
10 | as various ways to read the data with `getX`, `allX`, and through connections.
11 |
12 | Scaphold allows you to write GraphQL queries that are compiled down to SQL which means you get powerful
13 | filtering abilities with `WhereArgs` as well as compound `orderBy` expressions. We even expose the ability
14 | for you to index certain fields in your data so that you can optimize the queries that are important to your
15 | application.
--------------------------------------------------------------------------------
/docs/coredata/mutations.md:
--------------------------------------------------------------------------------
1 | ## Mutations
2 |
3 | A GraphQL mutation is a write followed by a fetch in one operation.
4 |
5 | Mutations are your means of modifying data in your API. Each core data type `X`, gets a `createX`, `updateX`, and `deleteX`
6 | mutation. Input arguments are automatically created to fit your schema and can be inspected from GraphiQL's Doc Explorer.
7 |
8 | Here's an example of each type of mutation:
9 |
10 | ### Create
11 |
12 | !!! tip ""
13 |
14 | Example request to create a user
15 |
16 | ```shell
17 | curl -X POST https://us-west-2.api.scaphold.io/graphql/scaphold-graphql \
18 | -H "Content-Type: application/json" \
19 | -d '{ "query": "mutation CreateUser($user: CreateUserInput!) { createUser(input: $user) { changedUser { id username } } }",
20 | "variables": { "user": { "username": "elon@tesla.com", "password": "SuperSecretPassword" } } }'
21 | ```
22 |
23 | ```javascript
24 | import request from 'request';
25 |
26 | const data = {
27 | "query": `
28 | mutation CreateUser($user: CreateUserInput!) {
29 | createUser(input: $user) {
30 | changedUser {
31 | id
32 | username
33 | }
34 | }
35 | }
36 | `,
37 | "variables": {
38 | "user": {
39 | "username": "elon@tesla.com",
40 | "password": "SuperSecretPassword"
41 | }
42 | }
43 | };
44 |
45 | request({
46 | url: "https://us-west-2.api.scaphold.io/graphql/scaphold-graphql",
47 | method: "POST",
48 | json: true,
49 | headers: {
50 | "content-type": "application/json",
51 | },
52 | body: data
53 | }, (error, response, body) => {
54 | if (!error && response.statusCode == 200) {
55 | console.log(JSON.stringify(body, null, 2));
56 | } else {
57 | console.log(error);
58 | console.log(response.statusCode);
59 | }
60 | });
61 | ```
62 |
63 | The above command returns an object structured like this:
64 |
65 | ```json
66 | {
67 | "data": {
68 | "createUser": {
69 | "changedUser": {
70 | "id": "VXNlcjo3",
71 | "username": "elon@tesla.com"
72 | }
73 | }
74 | }
75 | }
76 | ```
77 |
78 | !!! warning ""
79 |
80 | The request above may throw an error since there's a **uniqueness constrainst set automatically** on the User model's `username` field.
81 |
82 | In this request, JSON-formatted variables are used to send an object as part of the payload for the mutation request.
83 | The dollar sign ($) specifies a GraphQL variable, and the variable definition can be found in the variables section
84 | of the request. Another thing to note here is that when introducing variables in the mutation string,
85 | `$user: CreateUserInput!` means that that variable is of type `CreateUserInput!` and is required (!).
86 |
87 | ### Nested Create
88 |
89 | As an added convenience you are able to create objects and associate them in the same request.
90 |
91 | For example, let's say we were making an app to help friends manage group trips. For our app we would
92 | have a schema where `User`s have `Group`s and `Group`s have a `Trip`. If we were starting a new
93 | `Group` then we would likely want to start a new `Trip` at the same time. then you
94 | could create both a `Group` and a `Trip` as well as associate them in a connection with this query.
95 |
96 | !!! tip ""
97 |
98 | Example request to create a trip and group, then relate them simultaneously:
99 |
100 | ```graphql
101 | mutation CreateNested($input: CreateTripInput!) {
102 | createTrip(input: $input) {
103 | changedTrip {
104 | destination
105 | group {
106 | name
107 | }
108 | }
109 | }
110 | }
111 | ```
112 |
113 | Notice the nested `group` field in the input variable:
114 |
115 | ```
116 | {
117 | "trip": {
118 | "destination": "San Francisco",
119 | "group": {
120 | "name": "Seattlites"
121 | }
122 | }
123 | }
124 | ```
125 |
126 | !!! note ""
127 |
128 | If you provide an object id as well as a nested input argument, the object id will take precedence
129 | and the nested object will not be created. In the example above, there would also be a field
130 | `groupId` of type ID in the `CreateTripInput` type. If you were to provide both a `group` and `groupId`,
131 | the `groupId` would take precendence and the new trip would be associated with the existing group
132 | with that id instead of making a new group.
133 |
134 | ### Update
135 |
136 | You can also make a mutation to update data. The update operation performs a non-destructive update
137 | to an object in your dataset. I.E. Update only updates the fields that you include as part of the
138 | input. The object's `id` is required in every update mutation so that we can uniquely identify the
139 | object you would like to update. If you don't know it, you should perform a query operation to fetch
140 | the data first, or save it in your application's state after creating the object.
141 |
142 | !!! tip ""
143 |
144 | Example request to update a user
145 |
146 | ```shell
147 | curl -X POST https://us-west-2.api.scaphold.io/graphql/scaphold-graphql \
148 | -H "Content-Type: application/json" \
149 | -d '{ "query": "mutation UpdateUser($user: UpdateUserInput!) { updateUser(input: $user) { changedUser { id username biography } } }",
150 | "variables": { "user": { "id": "VXNlcjox", "biography": "Spends his days saving the world with renewable energy." } } }'
151 | ```
152 |
153 | ```javascript
154 | import request from 'request';
155 |
156 | const data = {
157 | "query": `
158 | mutation UpdateUser($user: UpdateUserInput!) {
159 | updateUser(input: $user) {
160 | changedUser {
161 | id
162 | username
163 | biography
164 | }
165 | }
166 | }
167 | `,
168 | "variables": {
169 | "user": {
170 | "id": "VXNlcjox",
171 | "biography": "Spends his days saving the world with renewable energy."
172 | }
173 | }
174 | };
175 |
176 | request({
177 | url: "https://us-west-2.api.scaphold.io/graphql/scaphold-graphql",
178 | method: "POST",
179 | json: true,
180 | headers: {
181 | "content-type": "application/json",
182 | },
183 | body: data
184 | }, (error, response, body) => {
185 | if (!error && response.statusCode == 200) {
186 | console.log(JSON.stringify(body, null, 2));
187 | } else {
188 | console.log(error);
189 | console.log(response.statusCode);
190 | }
191 | });
192 | ```
193 |
194 | The above command returns an object structured like this:
195 |
196 | ```json
197 | {
198 | "data": {
199 | "updateUser": {
200 | "changedUser": {
201 | "id": "VXNlcjox",
202 | "username": "Elon Musk",
203 | "biography": "Spends his days saving the world with renewable energy."
204 | }
205 | }
206 | }
207 | }
208 | ```
209 |
210 | !!! note "Managing Connection IDs"
211 |
212 | For connections, in order to update a connection field's ID to remove the connected object, you should set the related ID field to `""` (empty string, double quotes).
213 |
214 | ### Delete
215 |
216 | Use delete operations to delete data from your API. Delete requires the unique global identifier `id`
217 | of the piece of data that you wish to delete. Upon deleting data, you will receive the data back one
218 | last time in case you need it again, and to serve as confirmation that that particular object was removed.
219 |
220 | !!! tip ""
221 |
222 | Example request to delete a user
223 |
224 | ```shell
225 | curl -X POST https://us-west-2.api.scaphold.io/graphql/scaphold-graphql \
226 | -H "Content-Type: application/json" \
227 | -d '{ "query": "mutation DeleteUser($user: DeleteUserInput!) { deleteUser(input: $user) { changedUser { id username } } }",
228 | "variables": { "user": { "id": "VXNlcjo4" } } }'
229 | ```
230 |
231 | ```javascript
232 | import request from 'request';
233 |
234 | const data = {
235 | "query": `
236 | mutation DeleteUser($user: DeleteUserInput!) {
237 | deleteUser(input: $user) {
238 | changedUser {
239 | id
240 | username
241 | }
242 | }
243 | }
244 | `,
245 | "variables": {
246 | "user": {
247 | "id": "VXNlcjo4"
248 | }
249 | }
250 | };
251 |
252 | request({
253 | url: "https://us-west-2.api.scaphold.io/graphql/scaphold-graphql",
254 | method: "POST",
255 | json: true,
256 | headers: {
257 | "content-type": "application/json",
258 | },
259 | body: data
260 | }, (error, response, body) => {
261 | if (!error && response.statusCode == 200) {
262 | console.log(JSON.stringify(body, null, 2));
263 | } else {
264 | console.log(error);
265 | console.log(response.statusCode);
266 | }
267 | });
268 | ```
269 |
270 | The above command returns an object structured like this:
271 |
272 | ```json
273 | {
274 | "data": {
275 | "deleteUser": {
276 | "changedUser": {
277 | "id": "VXNlcjo4",
278 | "username": "elon@spacex.com"
279 | }
280 | }
281 | }
282 | }
283 | ```
284 |
285 | !!! warning ""
286 |
287 | The request above may return `null`. This is an indication that you tried to delete a piece of data
288 | that doesn't exist.
--------------------------------------------------------------------------------
/docs/coredata/schema.md:
--------------------------------------------------------------------------------
1 | ## The Schema
2 |
3 | Every application you build on Scaphold will have a schema. A schema consists of a set of types and a set of
4 | connections between those types. This connected web of types creates the 'graph' that will come to define your API.
5 |
6 | One of GraphQL's biggest benefits is that it provides a mechanism to express types at the API level. Types have
7 | existed in general purpose programming languages and other query languages like SQL for years so it's about
8 | time we brought it to APIs.
9 |
10 | There are **two main classes of types** that you need to be aware of on Scaphold.
11 |
12 | 1. Types that implement the `Node` interface.
13 | These are first-class entities in your Scaphold API. We will generate queries and mutations for them, you may create
14 | connections between them, and you may use them with event-based integrations.
15 |
16 | 2. Types that *don't* implement the `Node` interface.
17 | These are auxiliary structures in your GraphQL API and these objects are stored inline within other `Node` types.
18 | Scaphold will not create queries and mutations for non-node types nor can you use them to fire event-based integrations.
19 | You can, however, still relate them to other objects via the `List` type or as an inline object.
20 |
21 | Scaphold's [Schema Designer](https://scaphold.io/apps) allows you to define complex schemas in a fraction of the
22 | time while also offering you a convenient place to setup access control rules ([permissions](#permissions)) for your
23 | data. As you define the structure of your schema we'll generate the backend that will host your custom GraphQL API. By
24 | default we generate a `get`, `create`, `update`, and `delete` operation for each type in your schema. And to give you that
25 | extra boost, we also create mutations to handle user authentication, schema migration, and integration management.
26 |
27 | !!! tip ""
28 |
29 | Example schema
30 |
31 | ```graphql
32 | type ScapholdSchema {
33 | id: ID!
34 | name: String!
35 | description: String
36 | types: [ScapholdType]
37 | }
38 |
39 | type ScapholdType {
40 | id: ID!
41 | name: String!
42 | description: String
43 | kind: "OBJECT" | "ENUM" | "INTERFACE"
44 | values: [String] # if ENUM
45 | fields: [ScapholdField] # if INTERFACE or OBJECT
46 | interfaces: [String] # if OBJECT
47 | permissions: [ScapholdPermission]
48 | }
49 |
50 | type ScapholdField {
51 | name: String!
52 | description: String
53 | type: String!
54 | ofType: String
55 | nonNull: Boolean
56 | unique: Boolean
57 | reverseName: String
58 | }
59 |
60 | interface Node {
61 | id: ID!
62 | }
63 | ```
64 |
65 | ### Scalars
66 |
67 | Name | Description
68 | -------------- | --------------
69 | ID | The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an `ID` signifies that it is not intended to be human‐readable.
70 | String | A UTF‐8 character sequence
71 | Text | Character sequence with unlimited length (cannot be indexed or have a default value)
72 | Int | A signed 32-bit integer
73 | Float | A signed double-precision floating-point value
74 | Boolean | `true` or `false`
75 | DateTime | Valid timestamp that can be converted to standard ISO 8601 date time format.
76 |
77 | ### Types
78 |
79 | Scaphold provides 3 objects types to start off with: User, Role, and File.
80 |
81 | ```graphql
82 | type User implements Node {
83 | id: ID!
84 | username: String!
85 | password: String!
86 | roles: RoleConnection
87 | createdAt: DateTime!
88 | modifiedAt: DateTime!
89 | lastLogin: DateTime!
90 | }
91 |
92 | type Role implements Node {
93 | id: ID!
94 | name: String!
95 | members: UserConnection
96 | createdAt: TimeStamp!
97 | modifiedAt: TimeStamp!
98 | }
99 |
100 | type File implements Node, Blob {
101 | id: ID!
102 | name: String
103 | blobUrl: String
104 | blobMimeType: String
105 | createdAt: DateTime!
106 | modifiedAt: DateTime!
107 | }
108 | ```
109 |
110 | Scaphold provides 3 interfaces to start off with: Node, Timestamped, and Blob.
111 |
112 | ```graphql
113 | interface Node {
114 | id: ID!
115 | }
116 |
117 | interface Timestamped {
118 | createdAt: DateTime!
119 | modifiedAt: DateTime!
120 | }
121 |
122 | interface Blob {
123 | blobUrl: String
124 | blobMimeType: String
125 | }
126 | ```
127 |
128 | Scaphold provides 1 enum off the bat called AccessLevel.
129 |
130 | ```graphql
131 | enum AccessLevel {
132 | readonly
133 | readwrite
134 | admin
135 | }
136 | ```
137 |
138 | Name | Description
139 | -------------- | --------------
140 | Object | These are the bread and butter of the GraphQL type system and schema. They are the model definitions of your data, telling the GraphQL server what types exist, the fields that belong to a type, and the complex relations between your data. GraphQL queries are validated against this type system. Scaphold allows you to easily define your types without having to write any server-side code, so you can leverage the benefits of GraphQL from your client side, without having to write this all yourself.
141 | Interface | An interface is an abstract type that must be implemented by a type. The fields belonging to an interface must also exist on the type that implements it.
142 | Enum | Enums are special scalar types that restrict values to a finite set of data.
143 |
144 | **Adding a Type**
145 |
146 | In the Schema Designer tab once you've created your app, click the dropdown on the top right to add a type.
147 |
148 |
149 |
150 | ### Type Modifiers
151 |
152 | Name | Description
153 | -------------- | --------------
154 | List | A List indicates that this field will return an array of a particular type. This is denoted in GraphQL as `[ ... ]` surrounding the name of a type or scalar.
155 | Non-Null | The Non-Null type modifier can also be used when defining arguments for a field, which will cause the GraphQL server to return a validation error if a null value is passed as that argument, whether in the GraphQL string or in the variables. This is denoted in GraphQL as a `!` following the name of a type or scalar.
156 |
157 | ### Fields
158 |
159 | In GraphQL, like many other type systems, each type will have a set of fields associated with it. Fields are used to describe a particular type, and types in Scaphold will come by default
160 | with an `id`, `createdAt`, and `modifiedAt` fields as part of implementing the `Node` and `Timestamped` interfaces. These can be removed when creating a new type; however, a type must have at least one field.
161 |
162 | **Adding a Field**
163 |
164 | In the Schema Designer tab once you've created your type, click the dropdown on the top right of a Type panel.
165 |
166 |
167 |
--------------------------------------------------------------------------------
/docs/coredata/subscriptions.md:
--------------------------------------------------------------------------------
1 | ## Subscriptions
2 |
3 | When Facebook open-sourced GraphQL, they described how applications can perform reads
4 | with queries, and writes with mutations. However, oftentimes clients want to get pushed
5 | updates from the server when data they care about changes. Enter Subscriptions. **Subscriptions
6 | make real-time functionality a first class citizen in GraphQL!**
7 |
8 | Subscriptions offer a clean and efficient way to get pushed updates in real-time. They act
9 | in parallel to mutations. Just like how mutations describe the set of actions you can
10 | take to change your data, subscriptions define the set of events that you can subscribe
11 | to when data changes. In fact, you can think of subscriptions as a way to react to
12 | mutations made elsewhere.
13 |
14 | For example, think about a chat application like **Slack**. To create a good user experience,
15 | our application needs to stay up to date at all times. I.E. when a co-worker sends me a message,
16 | I shouldn't have to refresh the page to see the message. A much better solution is to have the
17 | server push my chat client the message as soon as it is created. This is how subscriptions work.
18 | When someone creates a message (or in other words issues a mutation), the server immediately
19 | pushes the data to every client that is both subscribed to that event.
20 |
21 | !!! warning ""
22 |
23 | **GraphQL Subscriptions require a web socket connection.** This is client-specific. We use Apollo Client
24 | for our web apps as they have functionality to add a web socket handler to their base network interface.
25 |
26 | !!! tip ""
27 |
28 | Example request to subscribe and get a real-time feed of when any user logs in or is created
29 |
30 | > Query
31 |
32 | ```graphql
33 | subscription SubscribeToUser($user: [UserMutationEvent]!) {
34 | subscribeToUser(mutations: $user) {
35 | mutation
36 | value {
37 | id
38 | username
39 | }
40 | }
41 | }
42 | ```
43 |
44 | > Variables
45 |
46 | ```json
47 | {
48 | "user": [
49 | "loginUser",
50 | "createUser"
51 | ]
52 | }
53 | ```
54 |
55 | ### Subscriptions in GraphiQL
56 |
57 | > Query
58 |
59 | ```graphql
60 | subscription SubscribeToNewMessages($messageFilter:MessageSubscriptionFilter) {
61 | subscribeToMessage(mutations:[createMessage], filter:$messageFilter) {
62 | mutation
63 | value {
64 | id
65 | content
66 | channel {
67 | id
68 | name
69 | }
70 | createdAt
71 | }
72 | }
73 | }
74 | ```
75 |
76 | > Variables
77 |
78 | ```json
79 | {
80 | "messageFilter": {
81 | "content": {
82 | "matches": ".\*GraphQL.\*"
83 | },
84 | "channelId": {
85 | "eq": "SavedChannelId"
86 | }
87 | }
88 | }
89 | ```
90 |
91 | You can play around with Subscriptions in our GraphiQL page! It's hooked up to handle web socket connections, so Subscription
92 | requests will work immediately. Normal HTTP clients won't work with Subscriptions since it requires a web socket connection.
93 |
94 |
95 |
96 | This is what I'm doing:
97 |
98 | 1) I create a `Channel` object with name `GraphQL News!` and save its id.
99 |
100 | 2) By issuing the query to the right, I subscribe to all new messages that are created in the `GraphQL News!` channel
101 | and that have content matching the regex `.*GraphQL.*`
102 |
103 | 3) I send a `Message` with content `GraphQL is future!` to channel `GraphQLNews!` and see a message pushed into the Subscription stream.
104 |
105 | 4) I send a `Message` with content `REST is dead!` to channel `GraphQLNews!` and no message appears in the Subscription stream.
106 |
107 | 5) I send a `Message` with content `GraphQL Subscriptions are Awesome!` to channel `GraphQLNews!` and another message appears in the Subscription stream.
108 |
109 | 6) I close the Subscription by double-tapping the pulse icon in the subscription stream.
110 |
111 | Subscribing to changes to data in your API is that easy! There are a number of subscription filters you can use to
112 | fine tune your subscriptions and you can even filter on One-To-Many and One-to-One connections using the associated
113 | id filters.
114 |
115 | Our Subscription implementation works (*almost*) out of the box with Apollo Client. Follow these
116 | steps to make your app real-time!
117 |
118 | ### Subscription Filters
119 |
120 | Scaphold exposes `SubscriptionFilter` arguments to subscription calls in your API so you can specify
121 | fine-grained conditions for when your subscription should fire. There are a lot of options available
122 | for you to use in `SubscriptionFilters`
123 |
124 | You will get the following operators for each scalar field in the type you are subscribing to.
125 |
126 | ```graphql
127 | type XSubscriptionFilter {
128 | eq: String
129 | gt: String
130 | gte: String
131 | lt: String
132 | lte: String
133 | ne: String
134 | between: [String]
135 | notBetween: [String]
136 | in: [String]
137 | notIn: [String]
138 | like: String
139 | notLike: String
140 | matches: String
141 | notMatches: String
142 | isNull: Boolean
143 | }
144 | ```
145 |
146 | !!! note
147 |
148 | `like` and `notLike` accept SQL match syntax while `matches` and `notMatches` accepts JS Regex
149 | syntax.
150 |
151 | ### Subscribing to Connections
152 |
153 | Applications often need a way to subscribe to changes in objects that are somehow related to
154 | other objects in their API. For example, if you were building a chat application you might
155 | only want to subscribe to messages in a particular chat room or channel. Scaphold allows you to
156 | issue this type of subscription using a `SubscriptionFilter`.
157 |
158 | The example below shows how you can subscribe to all new messages for a particular channel
159 | in our [Slackr tutorial](https://scaphold.io/community/blog/2016-11-10-build-realtime-apps-with-subs/).
160 | Notice how we pass in a `MessageSubscriptionFilter` that contains a `channelId` field. These id based
161 | filters work with all One-To-Many and One-To-One connections in your GraphQL schema. As you add
162 | these connections in the Schema Designer, filter arguments will automatically appear in the relvant
163 | `SubscriptionFilter`.
164 |
165 | > Query
166 |
167 | ```graphql
168 | subscription SubscribeToNewMessages($messageFilter:MessageSubscriptionFilter) {
169 | subscribeToMessage(mutations:[createMessage], filter:$messageFilter) {
170 | mutation
171 | value {
172 | id
173 | content
174 | channel {
175 | id
176 | }
177 | createdAt
178 | }
179 | }
180 | }
181 | ```
182 |
183 | > Variables
184 |
185 | ```json
186 | {
187 | "messageFilter": {
188 | "channelId": {
189 | "eq": "MyBase64ChannelId"
190 | }
191 | }
192 | }
193 | ```
194 |
--------------------------------------------------------------------------------
/docs/css/base.css:
--------------------------------------------------------------------------------
1 | strong, b {
2 | font-family: Avenir-Heavy;
3 | }
4 |
5 | .md-typeset a, .md-nav__link.md-nav__link--active {
6 | color: #1DAAA0;
7 | }
8 |
9 | .md-typeset a:hover, .md-nav__link:hover {
10 | color: #50DDD3;
11 | }
12 |
13 | .md-typeset [id] .headerlink:focus, .md-typeset [id]:hover .headerlink:hover, .md-typeset [id]:target .headerlink {
14 | color: #50DDD3 !important;
15 | }
16 |
17 | .md-header {
18 | background-color: #1DAAA0;
19 | }
20 |
21 | .md-nav.md-nav--secondary {
22 | border-color: #1DAAA0;
23 | }
24 |
25 | .md-footer-nav, .md-footer-meta {
26 | background-color: #29324B;
27 | }
28 |
29 | .md-icon--home {
30 | background-image: url(/images/favicon.ico);
31 | }
32 |
33 | .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover,
34 | .md-typeset .codehilite::-webkit-scrollbar-thumb:hover {
35 | background-color: #1DAAA0;
36 | }
37 |
38 | @media only screen and (max-width: 76.1875em) {
39 | html .md-nav--primary .md-nav__title--site {
40 | background-color: #1DAAA0;
41 | }
42 | }
43 |
44 | @media only screen and (max-width: 59.9375em) {
45 | .md-nav__source {
46 | background-color: #00776D;
47 | }
48 | }
49 |
50 | /*.md-content {
51 | margin-right: 0;
52 | }*/
53 |
54 | .video-container {
55 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12), 0 3px 1px -2px rgba(0,0,0,.2);
56 | position: relative;
57 | padding-top: 25px;
58 | padding-bottom: 56.25%;
59 | height: 0;
60 | }
61 | .video-container iframe {
62 | position: absolute;
63 | top: 0;
64 | left: 0;
65 | width: 100%;
66 | height: 100%;
67 | }
--------------------------------------------------------------------------------
/docs/css/styles.css:
--------------------------------------------------------------------------------
1 | .codehilite .hll { background-color: #ffffcc }
2 | .codehilite .c { color: #999988; font-style: italic } /* Comment */
3 | .codehilite .err { color: #a61717; background-color: #e3d2d2 } /* Error */
4 | .codehilite .k { font-weight: bold } /* Keyword */
5 | .codehilite .o { font-weight: bold } /* Operator */
6 | .codehilite .cm { color: #999988; font-style: italic } /* Comment.Multiline */
7 | .codehilite .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
8 | .codehilite .c1 { color: #999988; font-style: italic } /* Comment.Single */
9 | .codehilite .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
10 | .codehilite .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
11 | .codehilite .ge { font-style: italic } /* Generic.Emph */
12 | .codehilite .gr { color: #aa0000 } /* Generic.Error */
13 | .codehilite .gh { color: #999999 } /* Generic.Heading */
14 | .codehilite .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
15 | .codehilite .go { color: #888888 } /* Generic.Output */
16 | .codehilite .gp { color: #555555 } /* Generic.Prompt */
17 | .codehilite .gs { font-weight: bold } /* Generic.Strong */
18 | .codehilite .gu { color: #aaaaaa } /* Generic.Subheading */
19 | .codehilite .gt { color: #aa0000 } /* Generic.Traceback */
20 | .codehilite .kc { font-weight: bold } /* Keyword.Constant */
21 | .codehilite .kd { font-weight: bold } /* Keyword.Declaration */
22 | .codehilite .kn { font-weight: bold } /* Keyword.Namespace */
23 | .codehilite .kp { font-weight: bold } /* Keyword.Pseudo */
24 | .codehilite .kr { font-weight: bold } /* Keyword.Reserved */
25 | .codehilite .kt { color: #445588; font-weight: bold } /* Keyword.Type */
26 | .codehilite .m { color: #009999 } /* Literal.Number */
27 | .codehilite .s { color: #bb8844 } /* Literal.String */
28 | .codehilite .na { color: #008080 } /* Name.Attribute */
29 | .codehilite .nb { color: #999999 } /* Name.Builtin */
30 | .codehilite .nc { color: #445588; font-weight: bold } /* Name.Class */
31 | .codehilite .no { color: #008080 } /* Name.Constant */
32 | .codehilite .ni { color: #800080 } /* Name.Entity */
33 | .codehilite .ne { color: #990000; font-weight: bold } /* Name.Exception */
34 | .codehilite .nf { color: #990000; font-weight: bold } /* Name.Function */
35 | .codehilite .nn { color: #555555 } /* Name.Namespace */
36 | .codehilite .nt { color: #000080 } /* Name.Tag */
37 | .codehilite .nv { color: #008080 } /* Name.Variable */
38 | .codehilite .ow { font-weight: bold } /* Operator.Word */
39 | .codehilite .w { color: #bbbbbb } /* Text.Whitespace */
40 | .codehilite .mf { color: #009999 } /* Literal.Number.Float */
41 | .codehilite .mh { color: #009999 } /* Literal.Number.Hex */
42 | .codehilite .mi { color: #009999 } /* Literal.Number.Integer */
43 | .codehilite .mo { color: #009999 } /* Literal.Number.Oct */
44 | .codehilite .sb { color: #bb8844 } /* Literal.String.Backtick */
45 | .codehilite .sc { color: #bb8844 } /* Literal.String.Char */
46 | .codehilite .sd { color: #bb8844 } /* Literal.String.Doc */
47 | .codehilite .s2 { color: #bb8844 } /* Literal.String.Double */
48 | .codehilite .se { color: #bb8844 } /* Literal.String.Escape */
49 | .codehilite .sh { color: #bb8844 } /* Literal.String.Heredoc */
50 | .codehilite .si { color: #bb8844 } /* Literal.String.Interpol */
51 | .codehilite .sx { color: #bb8844 } /* Literal.String.Other */
52 | .codehilite .sr { color: #808000 } /* Literal.String.Regex */
53 | .codehilite .s1 { color: #bb8844 } /* Literal.String.Single */
54 | .codehilite .ss { color: #bb8844 } /* Literal.String.Symbol */
55 | .codehilite .bp { color: #999999 } /* Name.Builtin.Pseudo */
56 | .codehilite .vc { color: #008080 } /* Name.Variable.Class */
57 | .codehilite .vg { color: #008080 } /* Name.Variable.Global */
58 | .codehilite .vi { color: #008080 } /* Name.Variable.Instance */
59 | .codehilite .il { color: #009999 } /* Literal.Number.Integer.Long */
--------------------------------------------------------------------------------
/docs/custom-logic/async-operations.md:
--------------------------------------------------------------------------------
1 | # Async Operations
2 |
3 | After the final post-operation function finishes, scaphold will immediately return the result to the client that made the request. This is great and keeps your APIs snappy for a friendly user experience. However, sometimes you have tasks that are either long-running or are completely independent of the client app. In our example, we wanted to send push notifications to everyone on the team as soon as the new member joins. This operation would involve paginating through a potentially long list of users and could take an indefinite amount of time.
4 |
5 | For these situations use async functions! Async functions behave exactly the same as post-operation functions except Scaphold will not wait for a result after calling out to them. This makes them ideal for long standing tasks and other actions like logging.
6 |
--------------------------------------------------------------------------------
/docs/custom-logic/composition.md:
--------------------------------------------------------------------------------
1 | # Function Composition
2 |
3 | Function composition is a technique that combines simple functions into more complicated ones. At Scaphold, we use function composition to create modular, testable, and maintainable GraphQL resolvers. Logic allows you to do the same.
4 |
5 | ---
6 |
7 | !!! tip "Example"
8 |
9 | Let's walk through an example of how you might build an app like slack. Let's say a new user was invited to create an account and to join a particular team. This would take a couple of steps.
10 |
11 | 1. A user signs up and includes the name of the team.
12 | 2. Before the user is created we validate the user's email then authorize that they can join the team.
13 | 3. The user object is persisted to your DB hosted on Scaphold.
14 | 4. After the user is created, we use the generated ID to add the user to the Many-To-Many connection with the team.
15 | 5. The new user is returned to the client along with the new connection to the team.
16 | 6. After the payload was delivered to the client, we asynchronously send push notifications to everyone in the team informing that a new user has joined.
17 |
18 | ---
19 |
20 | Complex workflows like this were exactly why we built Logic. Let's break down how we would use logic to build it.
--------------------------------------------------------------------------------
/docs/custom-logic/index.md:
--------------------------------------------------------------------------------
1 | # What is Logic?
2 |
3 | Logic enables you to extend your API with business logic hosted on your
4 | own infrastructure. Different apps have different workflows and logic lets you customize Scaphold to fit virtually any need. You can host your logic on-prem or in the cloud as long as its accessible over the internet and you can even authenticate requests with custom headers. If your looking for the quickest way to start, we recommend
5 | AWS Lambda,
6 | Azure Functions, or
7 | Webtask.io.
8 |
9 | Our logic system is based on the concept of
10 | function composition.
11 | Composition lets us combine small microservices to create extremely powerful workflows. Let's take a look at how they work.
--------------------------------------------------------------------------------
/docs/custom-logic/logs.md:
--------------------------------------------------------------------------------
1 | # Logs & Examples
2 |
3 | ## Logs
4 |
5 | That wraps up our Logic tutorial! As you start using Logic, please not that you can see logs for each of your logic functions using the 'View Logs' button in the table dropdown when you click on a Pre or Post operation buttons. You can also easily enable and disable functions at any time.
6 |
7 |
8 | We hope you enjoy Logic and we'd love to hear your feedback! As a final ode to our discussion of how you can use logic to help build Slack, we'd love for you to join our Slack. It's an awesome community of people building cool applications with GraphQL and we'd love to see you there!
9 |
10 | ## Example Functions on GitHub
11 |
12 | For example logic functions check out our GitHub page: Scaphold Logic Examples.
--------------------------------------------------------------------------------
/docs/custom-logic/operation-phase.md:
--------------------------------------------------------------------------------
1 | # The Operation Phase
2 |
3 | After all of your pre-operation functions have finished, we take the input and persist it to the storage layer.
4 |
5 |
6 | The operation phase is where Scaphold does everything it needs to persist the object. The operation
7 | phase often involves saving the object to the database, uploading files to blob storage, and firing
8 | events for subscriptions. The operation phase will also sometime involve integrations. For example,
9 | if you have turned on the Algolia integration, this phase handles indexing the object in your Algolia
10 | account for you.
11 |
--------------------------------------------------------------------------------
/docs/custom-logic/post-operations.md:
--------------------------------------------------------------------------------
1 | Post-operations behave very similarly to pre-operations with one exception. When we call out to post-operation functions, we include the result of the operation under the `payload` key. Post operations also define a payload selection set that they use to selectively grab fields from the mutation payload. They will also receive the metadata added by the functions before it in the chain (included the metadata from pre-operation functions).
2 |
3 | Using our same example, if our first post-operation function had this selection set:
4 |
5 | ```graphql
6 | {
7 | changedUser {
8 | id teamReferral
9 | }
10 | }
11 | ```
12 |
13 | Then the first post-operation function would receive a request body like this:
14 |
15 | ```javascript
16 | {
17 | "payload": {
18 | "changedUser": {
19 | "id": "xxxxxxxxx",
20 | "teamReferral": "SlackAdmins",
21 | }
22 | },
23 | "emailMetadata": {
24 | "company": "Slack, Inc."
25 | }
26 | }
27 | ```
28 |
29 | !!! note
30 |
31 | You cannot manipulate the payload directly but you can issue mutations as well
32 | as attach metadata. The post-operation functions are particularly useful for managing the
33 | connections of newly created objects.
34 |
35 | ## Managing Connections in Post Operations
36 |
37 | For our slack app, we said we wanted to have the user automatically join the team as soon as the user was created. To keep our frontend efficient, we should be able to grab both the newly created user as well as the team in a single call from the mutation payload. For example look
38 | at the query below.
39 |
40 | ```graphql
41 | mutation CreateUser($user: CreateUserInput!) {
42 | createUser(input:$user) {
43 | changedUser {
44 | id
45 | user
46 | teams {
47 | edges {
48 | node {
49 | id
50 | name
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
57 | ```
58 |
59 | Without logic, this mutation would return an empty array of edges after the `createUser` mutation. To fix this, we would add a post-operation logic function that grabs the newly created user id, searches for the team with the name SlackAdmins and then issues a `addToUserTeamsConnection` mutation to associate the objects. This happens synchronously so by the time our servers return the payload will include an edge for the new connection.
--------------------------------------------------------------------------------
/docs/custom-logic/pre-operations.md:
--------------------------------------------------------------------------------
1 | # Pre Operations
2 |
3 | Use pre-operation functions to validate and manipulate input before
4 | objects are persisted to the database. You can add as many functions to a mutation as you want.
5 |
6 |
7 | We will compose your logic functions such that output of one function is passed in as the input to the next function. For example let's go over a flow assuming that the `createUser` mutation was called with these variables:
8 |
9 |
10 | ```javascript
11 | {
12 | "input": {
13 | "username": "stewart@slack.com",
14 | "password": "supersecret",
15 | "teamReferral": "SlackAdmins"
16 | }
17 | }
18 | ```
19 |
20 | ## The Request Body
21 |
22 | Assuming the same input as before, the first microservice in your workflow would receive a request with the following body:
23 |
24 |
25 | ```javascript
26 | {
27 | "input": {
28 | "username": "stewart@slack.com",
29 | "password": "supersecret",
30 | "teamReferral": "SlackAdmins"
31 | }
32 | }
33 | ```
34 |
35 |
36 | At any step you can throw an http error to invalidate an input and we will propagate the error down to the client. If you want to manipulate the input you return an object under the `input` key in the http response. Whatever object you return in your http response will be passed on to the next function. This allows you to both manipulate the input as well as propagate metadata through your functions. After your last pre-operation function, we will take the object under the `input` key and pass it to the operation phase to be persisted.
37 |
38 | ## Manipulate Input and Pass Metadata
39 |
40 | If this were our original input:
41 |
42 | ```javascript
43 | {
44 | "input": {
45 | "username": "stewart@slack.com",
46 | "password": "supersecret",
47 | "teamReferral": "SlackAdmins"
48 | }
49 | }
50 | ```
51 |
52 | And our first function returned this payload:
53 |
54 | ```javascript
55 | {
56 | "input": {
57 | "username": "stewart@slack.com",
58 | "password": "supersecret",
59 | "teamReferral": "SlackAdmins",
60 | "emailVerified": true
61 | },
62 | "emailMetadata": {
63 | "company": "Slack, Inc."
64 | }
65 | }
66 | ```
67 |
68 | Then the next function in the composition would receive this body:
69 |
70 | ```javascript
71 | {
72 | "input": {
73 | "username": "stewart@slack.com",
74 | "password": "supersecret",
75 | "teamReferral": "SlackAdmins",
76 | "emailVerified": true
77 | },
78 | "emailMetadata": {
79 | "company": "Slack, Inc."
80 | }
81 | }
82 | ```
83 |
84 | ## Passing Input to the Operation Phase
85 |
86 | After the last pre-operation function, Scaphold will pass the object under the `input` key on to the operation phase where it will be persisted. Keep this in mind as the object under
87 | the input key should adhere to the mutation's input type. e.g. If we were calling the `createUser` mutation the object under the `input` key should adhere to the `CreateUserInput` type.
88 |
--------------------------------------------------------------------------------
/docs/errors.md:
--------------------------------------------------------------------------------
1 | # Errors
2 |
3 | Errors from the Scaphold API will be returned as a list under the `errors` attribute at the root of the response.
4 | I.E. Scaphold responses adhere to the `ScapholdResult` type.
5 |
6 | Error Code | Meaning
7 | ---------- | -------
8 | 200 | OK -- Everything is working as expected
9 | 400 | Bad Request -- There was an error in your request.
10 | 401 | Unauthorized -- Your API token is wrong or missing
11 | 402 | Payment Required -- You have exceeded the free pricing teir. Please input a payment option via the Account page.
12 | 403 | Forbidden -- You have a valid API token but you do not have permission to this page.
13 | 404 | Not Found -- The specified resource could not be found.
14 | 429 | Too Many Requests -- You have approached the 60 req/s limit. Please try to spread out your requests more evenly.
15 | 440 | Login Failure -- We either couldn't find a user with that email or your password was incorrect.
16 | 441 | Registration Failure -- Invalid registration information. E.G. a user with that username already exists.
17 | 500 | Internal Server Error -- We had a problem with our server. Please contact us via slack or try again later.
18 | 503 | Service Unavailable -- We're temporarially offline for maintanance. Happens very rarely.
19 |
20 | !!! note ""
21 |
22 | A typical Scaphold response
23 |
24 | ```
25 | type ScapholdResult {
26 | data: any
27 | errors: [ScapholdError]
28 | }
29 |
30 | type ScapholdError {
31 | message: string!
32 | code: ErrorCode
33 | param: string
34 | }
35 | ```
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | Can't find what you're looking for? Contact us on [Slack](http://slack.scaphold.io)!
4 |
5 | ### What can I build?
6 |
7 | Whether you're building a mobile or web app, we've got you covered. Scaphold will help you through every level of the development process. Use our schema designer to structure your data, GraphiQL to prototype your queries and export them directly into your client applications, monitor your application in our dashboard, and integrate popular services to extend your API.
8 |
9 | ### Can I trust Scaphold when a service like Parse shutdown?
10 |
11 | YES! We are Ycombinator backed company and are going strong! To help ease your mind, it is important to note that Facebook shut down Parse because it no longer fit in with their core strategy. There is a still a need for powerful BaaS products and we are here to bring you one.
12 |
13 | In the extremely unlikely event that we do shut down then you still don't need to worry! GraphQL is a 100% open spec and you have the freedom to build a GraphQL API just like the ones we expose to you! You might lose some of the bells and whistles such as our seamless integrations but your business will survive and your client application code will remain the same!
14 |
15 | ### How do we compare with other BaaS products?
16 |
17 | We think GraphQL is the best way to build apps. It's that simple. There are other existing services like Firebase and Kinvey but they suffer from the same problems that have been plaguing BaaS solutions for years. This means vendor lock-in, complicated REST APIs, limited SDKs, and lack of flexibility. Scaphold turns this around and presents you with a standardized, extensible, and flexible GraphQL backend that can fit all kinds of application needs. Our GraphQL API releases you from vendor lock-in, provides you with API that anyone can understand, exposes a richer query language, and allows you to extend your APIs functionality with our many integrations. On top of all of this, our pricing model is the best in the industry so you can squeeze as much out of every dollar as possible!
18 |
19 | ### What is our SLA?
20 |
21 | Although we do not currently expose a standard SLA, our services run on a distributed cluster that was designed to prevent a single point of failure from bringing down our services. In the unlikely event that our services do go down for a time, we have systems working around the clock that will let us know so we can quickly fix the problem. You can trust that we are doing everything we can to provide the best possible experience for our clients and will continually push for 6 nines.
22 |
23 | ### Who is using GraphQL?
24 |
25 | The biggest proponent of GraphQL by far is Facebook. They designed GraphQL to solve many of the problems that they experienced when building many of their web and mobile products and they are using the technology to power more and more of their products. It's not just Facebook however! Companies like Twitter, Meteor, and Kadira are also using GraphQL heavily and the feedback has been overwhelmingly positive.
26 |
27 | ### How do I get support?
28 |
29 | It's our goal to make your life as a developer as easy as possible and that means haveing great customer support. The best way to get support is either using the Intercom chat bubble on this page or to join us on Slack at [Scaphold Slack](http://slack.scaphold.io). Please feel free to contact us at any hour of the day and we will respond as soon as possible!
30 |
31 | ### Resources
32 |
33 | - Facebook's [GraphQL.org](http://graphql.org)
34 |
35 | - Kadira's [LearnGraphQL.com](https://learngraphql.com/)
36 |
37 | - Apollo's [GraphQL.com](http://www.graphql.com/)
38 |
39 | - [The Fastest Way to Get Started](https://scaphold.io/community/blog/fastest-way-to-get-started/)
40 |
41 | - [GraphQL vs. REST](https://medium.com/chute-engineering/graphql-in-the-age-of-rest-apis-b10f2bf09bba#.el99enbvh)
42 |
43 | - [State of GraphQL](https://scaphold.io/community/blog/state-of-graphql/)
44 |
45 | - Clay Allsopp's [GraphQLHub.com](https://www.graphqlhub.com/)
46 |
47 | - [Official GraphQL Slack](https://graphql-slack.herokuapp.com/)
48 |
49 | - [Scaphold Slack](http://slack.scaphold.io)
50 |
51 | - [Other Community Sites](http://graphql.org/community/)
52 |
53 | - [Unofficial List of Resources](https://github.com/chentsulin/awesome-graphql)
54 |
55 | If you have other additions to this list that you'd like to add, feel free to contact us at our [Slack channel](http://slack.scaphold.io). Message either @vince or @michael.
56 |
57 | ### Look Out For...
58 |
59 | - More public GraphQL APIs
60 | - Relay 2.0!
61 | - GraphQL Subscriptions updates
62 | - Open source tooling released by companies that are using it internally
63 | - More mobile support
64 |
65 |
--------------------------------------------------------------------------------
/docs/files/index.md:
--------------------------------------------------------------------------------
1 | # Files
2 |
3 | !!! note ""
4 |
5 | All Scaphold APIs come with blob storage by default and all paid apps will be served files through a globally distributed CDN.
6 |
7 | File support in Scaphold is cooked into your schema. That means that any type that implements the `Blob`
8 | interface can be associated with a file stored in our distributed blob storage. When you create
9 | an app, we start you off with both the `Blob` interface as well as a `File` type. The `File` type implements
10 | `Blob` and `Node` and thus can both be connected to other types in your schema via Connection fields as well
11 | as handle file uploads and downloads.
--------------------------------------------------------------------------------
/docs/files/querying.md:
--------------------------------------------------------------------------------
1 | # Querying Files
2 |
3 | Querying files acts the same as other types. All types that implement `Blob` have a field `blobUrl` and `blobMimeType`
4 | that are automatically managed by Scaphold. The `blobUrl` is a pre-signed URL that points to your file in a private
5 | blob store hosted on Amazon S3. If youre app is on a paid tier, all pre-signed URLs will be served by
6 | a globally distributed CDN hosted by Amazon CloudFront.
7 |
8 | !!! tip ""
9 |
10 | Example of querying for a file
11 |
12 | !!! note ""
13 |
14 | File ID: `RmlsZTo5`
15 |
16 | ## cURL
17 |
18 | ```shell
19 | curl -X POST https://us-west-2.api.scaphold.io/graphql/scaphold-graphql \
20 | -H "Content-Type: application/json" \
21 | -d '{ "query": "query GetFile { getFile(id: \"RmlsZTo5\") { id name blobMimeType blobUrl user { id username } } }",
22 | "variables": {} }'
23 | ```
24 |
25 | ## JavaScript
26 |
27 | ```javascript
28 | var request = require('request');
29 |
30 | var data = {
31 | "query": `
32 | query GetFile {
33 | getFile(id: "RmlsZTo5") {
34 | id
35 | name
36 | blobMimeType
37 | blobUrl
38 | user {
39 | id
40 | username
41 | }
42 | }
43 | }
44 | `,
45 | "variables": {}
46 | }
47 |
48 | request({
49 | url: "https://us-west-2.api.scaphold.io/graphql/scaphold-graphql",
50 | method: "POST",
51 | json: true,
52 | headers: {
53 | "content-type": "application/json",
54 | },
55 | body: data
56 | }, function(error, response, body) {
57 | if (!error && response.statusCode == 200) {
58 | console.log(JSON.stringify(body, null, 2));
59 | } else {
60 | console.log(error);
61 | console.log(response.statusCode);
62 | }
63 | });
64 | ```
65 |
66 | ---
67 |
68 | The above command returns an object structured like this:
69 |
70 | ```json
71 | {
72 | "data": {
73 | "getFile": {
74 | "id": "RmlsZTo5",
75 | "name": "Mark Zuck's Profile Picture",
76 | "blobMimeType": "image/jpeg",
77 | "blobUrl": "https://s3-us-west-2.amazonaws.com/production.us-west-2.scaphold.v2.customer/44be086f-bf33-4997-8136-9c01d99a88c4/data/2fb4b11d-cef9-465d-9ad6-e3d8b693f121/2b86488a-7114-4071-9e30-157855475eb7?AWSAccessKeyId=AKIAJIC3JY2ICINJH2OQ&Expires=1481690209&Signature=icwTcNl%2B%2BTwQy8Ar6jLuquztwu0%3D",
78 | "user": {
79 | "id": "VXNlcjoxMA==",
80 | "username": "Mark Zuckerberg"
81 | }
82 | }
83 | }
84 | }
85 | ```
--------------------------------------------------------------------------------
/docs/files/uploading.md:
--------------------------------------------------------------------------------
1 | # Uploading Files
2 |
3 | Uploading files is simple. All you need to do is attach the file to a multipart/form-data request
4 | and point to it using the `blobFieldName` attribute in the `Blob` implemented type's input object.
5 |
6 | All types that implement `Blob` will receive an additional input field called `blobFieldName`.
7 |
8 | !!! tip ""
9 |
10 | Example file upload
11 |
12 | !!! note ""
13 |
14 | Mark Zuckerberg's user ID: `VXNlcjoxMA==`
15 |
16 | ## cURL
17 |
18 | ```shell
19 | curl -v https://us-west-2.api.scaphold.io/graphql/scaphold-graphql \
20 | -H "Content-Type:multipart/form-data" \
21 | -F 'query=mutation CreateFile($input: CreateFileInput!) { createFile(input: $input) { changedFile { id name blobMimeType blobUrl user { id username } } } }' \
22 | -F 'variables={ "input": { "name": "Profile Picture", "userId": "VXNlcjoxMA==", "blobFieldName": "myBlobField" } };type=application/json' \
23 | -F myBlobField=@mark-zuckerberg.jpg
24 | ```
25 |
26 | ## JavaScript
27 |
28 | ```javascript
29 |
30 | // You must also install these npm modules:
31 | // npm install --save form-data
32 | // npm install --save node-fetch
33 |
34 | var fetch = require('node-fetch');
35 | var FormData = require('form-data');
36 | var fs = require('fs');
37 |
38 | var form = new FormData();
39 | form.append("query", `
40 | mutation CreateFile($input: CreateFileInput!) {
41 | createFile(input: $input) {
42 | changedFile {
43 | id
44 | name
45 | blobMimeType
46 | blobUrl
47 | user {
48 | id
49 | username
50 | }
51 | }
52 | }
53 | }
54 | `);
55 | form.append("variables", JSON.stringify({
56 | "input": {
57 | "name": "Mark Zuck Profile Picture",
58 | "userId": "VXNlcjoxMA==",
59 | "blobFieldName": "myBlobField"
60 | }
61 | }));
62 | // The file's key matches the value of the field `blobFieldName` in the variables
63 | form.append("myBlobField", fs.createReadStream('./mark-zuckerberg.jpg'));
64 |
65 | fetch("https://us-west-2.api.scaphold.io/graphql/scaphold-graphql", {
66 | method: 'POST',
67 | body: form
68 | }).then(function(res) {
69 | return res.text();
70 | }).then(function(body) {
71 | console.log(body);
72 | });
73 | ```
74 |
75 | ## React Native
76 |
77 | ```javascript
78 | // You must also install these npm modules:
79 | // npm install --save react-native-fetch-blob
80 | // react-native link react-native-fetch-blob
81 | import RNFetchBlob from 'react-native-fetch-blob';
82 |
83 | RNFetchBlob.fetch(
84 | 'POST',
85 | "https://us-west-2.api.scaphold.io/graphql/scaphold-graphql",
86 | {
87 | Authorization : "Bearer " + scapholdAuthToken,
88 | 'Content-Type' : 'multipart/form-data',
89 | },
90 | [
91 | {
92 | name : 'query',
93 | data : `
94 | mutation CreateFile($input: CreateFileInput!) {
95 | createFile(input: $input) {
96 | changedFile {
97 | id
98 | blobMimeType
99 | blobUrl
100 | user {
101 | id
102 | }
103 | }
104 | }
105 | }
106 | `
107 | },
108 | {
109 | name : 'variables',
110 | data : JSON.stringify({
111 | "input": {
112 | "name": "Mark Zuck Profile Picture",
113 | "userId": "VXNlcjoxMA==",
114 | "blobFieldName": "myBlobField"
115 | }
116 | })
117 | },
118 | {
119 | name : 'myBlobField',
120 | filename : 'profile',
121 | data: RNFetchBlob.wrap(filePath),
122 | },
123 | ]
124 | );
125 | ```
126 |
127 | ---
128 |
129 | The above command returns an object structured like this:
130 |
131 | ```json
132 | {
133 | "data": {
134 | "createFile": {
135 | "changedFile": {
136 | "id": "RmlsZTo5",
137 | "name": "Mark Zuck's Profile Picture",
138 | "blobMimeType": "image/jpeg",
139 | "blobUrl": "https://s3-us-west-2.amazonaws.com/production.us-west-2.scaphold.v2.customer/44be086f-bf33-4997-8136-9c01d99a88c4/data/2fb4b11d-cef9-465d-9ad6-e3d8b693f121/2b86488a-7114-4071-9e30-157855475eb7?AWSAccessKeyId=AKIAJIC3JY2ICINJH2OQ&Expires=1481686711&Signature=pa4QbkPCk%2BXlgSrKBWcRKsEckSs%3D",
140 | "user": {
141 | "id": "VXNlcjoxMA==",
142 | "username": "Mark Zuckerberg"
143 | }
144 | }
145 | }
146 | }
147 | }
148 | ```
--------------------------------------------------------------------------------
/docs/getting-started/community.md:
--------------------------------------------------------------------------------
1 | # [Community](https://scaphold.io/community/)
2 |
3 | ## [Slack](http://slack.scaphold.io)
4 |
5 | Join the Scaphold community Slack channel! This is where we all hang out :tada:
6 |
7 | ## [Featured](https://scaphold.io/community/projects/)
8 |
9 | Scaphold has featured projects that have been built from our community of amazing developers.
10 |
11 | ## [Learn](https://scaphold.io/community/learn/)
12 |
13 | Learn from a spectacular list of resources to get you ramped up on GraphQL, Raect, Apollo, and more. We aggregate articles from across entire
14 | GraphQL community to help you find the right learning experience for you.
15 |
16 | ## [Projects](https://scaphold.io/community/projects/)
17 |
18 | The Scaphold community builds new projects & starter kits all the time. Check them out on our
19 | community page! If you have a project you would like to feature let us know on our [Slack Community Channel](http://slack.scaphold.io).
20 |
21 | ## [Blog](https://scaphold.io/community/blog/)
22 |
23 | Follow our blog for the latest news on GraphQL, Scaphold, and more. We'll write about best practices, how to pair GraphQL with popular technologies that you might use already, and even what we're up to ourselves.
24 |
25 | ## [FAQ](https://scaphold.io/community/questions/)
26 |
27 | Have a question? It's probably been answered before! Make your way to Scaphold's FAQ to get quick answers on frequently asked questions.
--------------------------------------------------------------------------------
/docs/getting-started/quickstart-video.md:
--------------------------------------------------------------------------------
1 | # Quickstart Tutorial
2 |
3 | !!! tip ""
4 |
5 | In this tutorial, we will walk through how to build Slack with GraphQL in 15 minutes using [Scaphold](https://scaphold.io).
6 |
7 | ### Pre-requisites
8 |
9 | * [x] A computer
10 | * [x] 15 minutes
11 | * [x] Basic understanding of React
12 |
13 | ### Step-by-step Guide
14 |
15 | Follow along this video as we build Slackr, a real-time messaging app complete with social authentication.
16 |
17 |
18 |
19 |
20 |
21 | | Resource | Link |
22 | | ------------------------ | ----------- |
23 | | Full Slackr Tutorial | [**Guide**](https://scaphold.io/community/blog/build-realtime-apps-with-subs/) |
24 | | Source Code | [**GitHub**](https://github.com/scaphold-io/slackr-graphql-subscriptions-starter-kit) |
25 |
26 | ### Conclusion
27 |
28 | After you complete the tutorial, you should have your very own Slackr app ready to share with your friends!
29 |
30 | 
31 |
32 | :wave: If you have any questions, please reach out on [Scaphold's Community Slack channel](http://slack.scaphold.io).
--------------------------------------------------------------------------------
/docs/images/authentication/Add_Permissions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/authentication/Add_Permissions.png
--------------------------------------------------------------------------------
/docs/images/authentication/Relation_Permission.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/authentication/Relation_Permission.png
--------------------------------------------------------------------------------
/docs/images/coredata/Add_Field.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/coredata/Add_Field.png
--------------------------------------------------------------------------------
/docs/images/coredata/Add_Type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/coredata/Add_Type.png
--------------------------------------------------------------------------------
/docs/images/coredata/Join_Table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/coredata/Join_Table.png
--------------------------------------------------------------------------------
/docs/images/coredata/Many_To_Many.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/coredata/Many_To_Many.png
--------------------------------------------------------------------------------
/docs/images/coredata/One_To_Many.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/coredata/One_To_Many.png
--------------------------------------------------------------------------------
/docs/images/coredata/One_To_One.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/coredata/One_To_One.png
--------------------------------------------------------------------------------
/docs/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/favicon.ico
--------------------------------------------------------------------------------
/docs/images/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/home.png
--------------------------------------------------------------------------------
/docs/images/integrations/Add_Restaurant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Add_Restaurant.png
--------------------------------------------------------------------------------
/docs/images/integrations/Algolia_Index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Algolia_Index.png
--------------------------------------------------------------------------------
/docs/images/integrations/Algolia_Viewer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Algolia_Viewer.png
--------------------------------------------------------------------------------
/docs/images/integrations/Configure_Auth0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Configure_Auth0.png
--------------------------------------------------------------------------------
/docs/images/integrations/Configure_Auth0_Account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Configure_Auth0_Account.png
--------------------------------------------------------------------------------
/docs/images/integrations/Digits_Oauth_Echo_Headers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Digits_Oauth_Echo_Headers.png
--------------------------------------------------------------------------------
/docs/images/integrations/Geo_Query.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Geo_Query.png
--------------------------------------------------------------------------------
/docs/images/integrations/Get_Access_Token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Get_Access_Token.png
--------------------------------------------------------------------------------
/docs/images/integrations/Insert_Jwt_Header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Insert_Jwt_Header.png
--------------------------------------------------------------------------------
/docs/images/integrations/Link_Google_User.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Link_Google_User.png
--------------------------------------------------------------------------------
/docs/images/integrations/Login_User_With_Digits.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Login_User_With_Digits.png
--------------------------------------------------------------------------------
/docs/images/integrations/Restaurant_Geo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Restaurant_Geo.png
--------------------------------------------------------------------------------
/docs/images/integrations/Restaurant_Searchable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/integrations/Restaurant_Searchable.png
--------------------------------------------------------------------------------
/docs/images/management/Add_Teammate_Panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/management/Add_Teammate_Panel.png
--------------------------------------------------------------------------------
/docs/images/management/Add_Teammate_Settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/management/Add_Teammate_Settings.png
--------------------------------------------------------------------------------
/docs/images/management/Multi_Region.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/management/Multi_Region.png
--------------------------------------------------------------------------------
/docs/images/management/Set_Alias.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/management/Set_Alias.png
--------------------------------------------------------------------------------
/docs/images/quickstart/PQ Check Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/quickstart/PQ Check Icon.png
--------------------------------------------------------------------------------
/docs/images/quickstart/android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/quickstart/android.png
--------------------------------------------------------------------------------
/docs/images/quickstart/angular2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
--------------------------------------------------------------------------------
/docs/images/quickstart/cerebral.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/quickstart/cerebral.png
--------------------------------------------------------------------------------
/docs/images/quickstart/ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/quickstart/ios.png
--------------------------------------------------------------------------------
/docs/images/quickstart/reactnative.svg:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/docs/images/quickstart/vue2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scaphold-io/scaphold-docs/346735f841a7790c694a179852976f0e3cf15d8c/docs/images/quickstart/vue2.png
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Welcome
2 |
3 | ## Introduction
4 |
5 | Welcome! Scaphold is a GraphQL backend-as-a-service platform that bundles all the tools you need to quickly build production-grade applications.
6 | In a matter of minutes, you can create your own **GraphQL API** backed by our highly available cloud infrastructure hosted on AWS.
7 |
8 | !!! tip ""
9 |
10 |
11 |
[**Quickstart Video**](/#quickstart-video)
12 |
[**Join our Slack!**](http://slack.scaphold.io)
13 |
[**Need a starter kit?**](/starter-kits)
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | This is the best place to learn how to start rapidly building applications with GraphQL!
24 | Check out the introductory video tutorial and follow along as we build a realtime chat application
25 | with React, Apollo Client, and GraphQL.
26 |
27 | ---
28 |
29 | ## Quickstart
30 |
31 | !!! tip "Learn how to build a real-time chat application"
32 |
33 | In this tutorial, we will walk through how to build Slack with GraphQL in 15 minutes using [Scaphold](https://scaphold.io).
34 |
35 | | Resource | Link |
36 | | ------------------------ | ----------- |
37 | | Full Slackr Tutorial | [**Read the Guide**](https://scaphold.io/community/blog/build-realtime-apps-with-subs/) |
38 | | Source Code | [**Clone from GitHub**](https://github.com/scaphold-io/slackr-graphql-subscriptions-starter-kit) |
39 |
40 | ### Demo
41 |
42 | In a few minutes, you will have your very own chat app ready to share with your friends!
43 |
44 | 
45 |
46 | ### Quickstart Video
47 |
48 | Follow along this video as we build Slackr, a real-time messaging app complete with social authentication.
49 |
50 |
51 |
52 |
53 |
54 | :wave: If you have any questions, please reach out on [Scaphold's Community Slack channel](http://slack.scaphold.io).
--------------------------------------------------------------------------------
/docs/index2.md:
--------------------------------------------------------------------------------
1 | # Welcome
2 |
3 | ## Introduction
4 |
5 | Welcome! Scaphold is a GraphQL backend-as-a-service platform that bundles all the tools you need to quickly build production-grade applications.
6 | In a matter of minutes, you can create your own **customizable GraphQL API** backed by our highly available cloud infrastructure hosted on AWS.
7 |
8 | !!! tip ""
9 |
10 |
[**Need a starter kit?**](/getting-started/starter-kits)
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ## Why GraphQL?
24 |
25 | ### The Story
26 |
27 | [GraphQL](http://graphql.org/) was developed by Facebook over the years to power and scale many of their mobile and web applications. A
28 | great deal of care was taken to ensure that GraphQL works from any client platform which means you can use Scaphold to build an application
29 | on virtually any platform. This means you can target your Scaphold API from iOS, Android, Web, and IOT applications using any framework
30 | **without ever having to download an SDK**! GraphQL presents a better way to build applications. It's that simple. You focus on what makes
31 | your app awesome and let us worry about the rest!
32 |
33 | ### How It Works
34 |
35 | 1. Describe your data
36 | ```graphql
37 | type Mission {
38 | id: ID!
39 | codename: String
40 | astronauts: [User]
41 | }
42 | ```
43 |
44 | 2. Ask for what you want
45 | ```graphql
46 | {
47 | getMission(id: "abc123") {
48 | codename
49 | }
50 | }
51 | ```
52 |
53 | 3. Get predictable results
54 | ```json
55 | {
56 | "getMission": {
57 | "codename": "GraphQL Takes Over The World!"
58 | }
59 | }
60 | ```
61 |
62 | !!! note
63 |
64 | All responses from your API will be formatted as JSON.
65 |
66 | ### GraphQL vs. REST
67 |
68 | GraphQL is a massive improvement on REST. Here is why!
69 |
70 | 1. A declarative type system helps create clean, structured, and safe APIs.
71 | 2. Write your queries once and run them anywhere! The intuitive GraphQL language is not only more powerful but also 100% cross-platform. No SDKs necessary!
72 | 3. Introspection lets you build documentation into the API itself. Your API easier to learn and eliminates the need to maintain external docs.
73 | 4. A GraphQL API has one endpoint. No more tricky RESTful urls.
74 | 5. Client driven. GraphQL empowers those who understand their requirements best. You!
75 | 6. It integrates seamlessly with the hottest frontend frameworks like React and Angular 2.0 using Relay and Apollo Client
76 | 7. And many many more!
77 |
--------------------------------------------------------------------------------
/docs/integrations/email.md:
--------------------------------------------------------------------------------
1 | # Email
2 |
3 | Use **Mailgun** to send and manage email from within your application. Bind emails to events to automate your workflow.
4 |
5 | 1. Create a free [Mailgun](https://mailgun.com/) account.
6 | 2. Add the Mailgun integration and enter your API key and domain name in Scaphold's Integrations Portal.
7 | 3. Start sending emails and managing mailing lists from your Scaphold API!
--------------------------------------------------------------------------------
/docs/integrations/index.md:
--------------------------------------------------------------------------------
1 | # Integrations
2 |
3 | Use Scaphold's integration infrastructure to tie third-party services into your API. When you add an integration, we will automatically update your API with new functionality.
4 | Integrations range in purpose. Want notifications? Add iOS Push. Want payments? Integrate Stripe. Need social auth? Add it with Auth0. Our collection of integrations is always expanding so please let us know if you have any specific requests.
5 |
6 | Integrations come in many shapes and sizes. Some integrations like Slack and Webhooks are powered by events in your API while others like Mailgun will expose their functionality by adding queries and mutations to your GraphQL API.
7 |
8 | We've got some exciting new integrations coming out soon! Please [let us know](mailto:support@scaphold.io) if you have any specific requests.
9 |
10 | !!! note ""
11 |
12 | To understand how many of these integrations work in detail, and what new queries and mutations your API will have after enabling a particular integration, you can **click on an integration panel** in the Integrations Portal to find out.
13 |
--------------------------------------------------------------------------------
/docs/integrations/monitoring.md:
--------------------------------------------------------------------------------
1 | # Monitoring
2 |
3 | Use **Apollo Optics** to add in-depth analytics for your API to understand how to optimize your queries.
4 |
5 | 1. Create an [Apollo Optics](http://www.apollostack.com/optics) account.
6 | 2. Add the Apollo Optics integration and enter your API key in Scaphold's Integration Portal.
7 |
8 | Every request you make will send information about how your data is being fetched, so you can view the performance of your all your requests through the comprehensive Optics dashboard.
9 |
10 | !!! tip "Tutorial"
11 |
12 | Follow this [quick guide](https://scaphold.io/community/blog/apollo-optics-and-your-graphql-server/).
--------------------------------------------------------------------------------
/docs/integrations/passwordless.md:
--------------------------------------------------------------------------------
1 | ## Passwordless Auth
2 |
3 | Use **Twitter Digits** to extend your app's authentication capabilities, and log in your users with passwordless authentication using their phone number.
4 |
5 | 1. Create a free Twitter Digits account online using [Fabric](https://docs.fabric.io/apple/digits/installation.html). This is the account management platform that Twitter uses to manage your Twitter integrations for your apps.
6 |
7 | 2. Once you've done so and installed Digits to your app, follow the steps on the [Digits Sandbox](https://docs.fabric.io/apple/digits/sandbox.html#) to configure your native app to test out the functionality in debug mode.
8 |
9 | 3. When you have this set up, you'll need to obtain [OAuth Echo Headers](https://docs.fabric.io/apple/digits/advanced-setup.html):
10 |
11 | 
12 |
13 | 4. Now you can make a request with these header values with the **new mutation called** `loginUserWithDigits` that will allow you to log in a user with this integration.
14 |
15 | It will return a **JWT token that you need to include in your future requests** as the bearer token for your Authorization header so that we can authenticate this particular logged in user.
16 |
17 | This works across authentication mechanisms on Scaphold, so you can link any number of social or passwordless authentication flows.
18 |
19 |
20 |
21 | !!! note ""
22 |
23 | We've built a little playground for you to test out passwordless auth using Twitter Digits in an iOS app.
24 |
25 | [View on GitHub](https://github.com/scaphold-io/ios-twitter-digits-playground)
26 |
27 | !!! note ""
28 |
29 | Please reference this example if you're unfamiliar with how to obtain OAuth Echo Headers.
30 |
31 | [Sample Code](https://fabric.io/kits/ios/digits/features)
--------------------------------------------------------------------------------
/docs/integrations/payments.md:
--------------------------------------------------------------------------------
1 | ## Payments
2 |
3 | Hook into **Stripe** to instantly add advanced payment methods to your application.
4 |
5 | The Stripe integration allows you to quickly and easily link your company's Stripe account with your Scaphold API.
6 |
7 | 1. Head to [Stripe](https://stripe.com/) and create a free account to get started.
8 | 2. Upload your secret key and publishable key via our [Integrations Portal](https://scaphold.io/apps).
9 | 3. Rejoice as we have just added tons of new payments functionality to your API.
--------------------------------------------------------------------------------
/docs/integrations/push.md:
--------------------------------------------------------------------------------
1 | ## Push Notifications
2 |
3 | ### Setup
4 |
5 | Add **iOS** or **Android** push notifications in an instant.
6 |
7 | 1. Follow these tutorials to get set up on each platform:
8 | - [iOS](https://scaphold.io/community/blog/iOS-push-notifications-with-graphql/)
9 | - [Android](https://scaphold.io/community/blog/android-push-notifications-with-graphql/)
10 | 2. Add the push integration for the platform of your choice and upload the appropriate keys through our Integrations Portal.
11 | 3. Immediately get access to iOS or Android push notifications via your app's GraphQL API.
12 |
13 | Once you've set up your integration, you can start managing device tokens across platforms and send push notification messages to valid device tokens and channels.
14 |
15 | ### Send Push Notification
16 |
17 | Follow along with [this guide on how to send push notifications with your GraphQL API](https://scaphold.io/community/blog/send-push-notifications-scaphold-graphql/)
18 | so you can start sending push notifications right away.
19 |
20 | ----
21 |
22 | We've also set up a couple boilerplates for you to help you get started on each platform:
23 |
24 | !!! tip "Boilerplates"
25 |
26 | - [iOS](https://github.com/scaphold-io/apple-ios-push-graphql-starter-kit)
27 |
28 | - [Android](https://github.com/scaphold-io/android-gcm-push-graphql-starter-kit)
--------------------------------------------------------------------------------
/docs/integrations/search.md:
--------------------------------------------------------------------------------
1 | ## Search
2 |
3 | !!! tip "Tutorial"
4 |
5 | Get started with the [full tutorial](https://scaphold.io/community/blog/algolia-powers-thousands-of-scaphold-apps/).
6 |
7 | ### Setup the Algolia Integration
8 |
9 | The Algolia integration lets you instantly add search capabilities to your API. As soon as you enable the integration and select the types
10 | that you would like indexed, we immediately push your data to Algolia. Scaphold will then make sure that your data held on our servers
11 | is kept in sync with your data on algolia.
12 |
13 | 1. Create a free [Algolia](https://www.algolia.com/) account. This will help you manage your search indices and monitor your usage. Once you've created your account, you'll receive an Application ID and an API Key.
14 |
15 | !!! note ""
16 |
17 | You can share a single algolia account for multiple apps. Each index we create for you is unique to the app and type being indexed.
18 |
19 | 2. Configure Algolia in Scaphold from the Integrations Portal and select the checkboxes in the popup to index data of that particular type.
20 |
21 | 
22 |
23 | 3. As soon as you enable search on a particular type, Scaphold will automatically index each object in your API
24 | as well as ensure that each create, update, and delete mutation is mirrored to Algolia. This makes id dead simple to combine
25 | the best of Algolia with the best of GraphQL and Scaphold.
26 |
27 | 4. You can use the Algolia portal to fine tune your indexes to improve the search experience for your apps.
28 |
29 | ### Full-Text Search
30 |
31 | Querying Algolia through your Scaphold API is super simple. For every type that you index you will receive a `searchAlgoliaX` query under the `viewer` in your API.
32 |
33 | For example, lets say we were building a note taking app like Evernote. A bare bones version of our app might include the types `User` and `Note`. In order to enable our users to search notes, we would turn on the Algolia integration and choose the `Note` type to be indexed. As soon as we turn on Algolia for the `Note` type we would be able to issue a query like that seen below.
34 |
35 | ```graphql
36 | query SearchNotes($searchTerm: String!) {
37 | viewer {
38 | searchAlgoliaNotes(query: $searchTerm, hitsPerPage: 10) {
39 | nbHits # how many hits
40 | nbPages # how many pages
41 | page # the current page
42 | hits {
43 | objectID
44 | _highlightResult # useful for clientside auto-suggest
45 | node { # the Note object.
46 | id
47 | content
48 | author { # we can still traverse connections
49 | id # even though `User` is not indexed.
50 | username
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
57 | ```
58 |
59 | !!! note ""
60 |
61 | Note how we are asking for the note's author despite not indexing our `User` type. When you let
62 | Scaphold manage your search data we are able to combine search with the connection in your GraphQL schema.
63 |
64 | These commands will be the same as before; however you now have a **new query for each indexed type under `Viewer`** that will allow you to search for query terms along with optional parameters.
65 |
66 | 
67 |
68 | ### Geo-Search
69 |
70 | Algolia helps us manage geo-indexes as well. Whenever you want to query data based on location, you can do so in multiple ways.
71 |
72 | !!! note "Example"
73 |
74 | Let's say we were trying to build Yelp and I wanted to find my nearby restaurants that are in a 5000 meter radius of Central Park in New York.
75 |
76 | #### Setup
77 |
78 | In order to set up a type for geo-indexing, there are two steps:
79 |
80 | 1. Add a field called `_geoloc` to the type `Restaurant` in the Schema Designer.
81 |
82 | 
83 |
84 | !!! note
85 |
86 | The `_geoloc` field can also be a list of `JSON` object types as well.
87 |
88 | 2. Index the type `Restaurant` as searchable in the Integrations Portal.
89 |
90 | 
91 |
92 | 3. Add a new restaurant.
93 |
94 |
95 |
96 | !!! warning ""
97 |
98 | The `lat` and `lng` fields must be `Float` types (i.e. they should not be a `String`).
99 |
100 | #### Querying
101 |
102 | Given the previous example, we want to search for restaurants near Central Park called Chipotle in a 5000 meter radius.
103 |
104 | !!! note ""
105 |
106 | Central Park coordinates: `40.7829412, -73.9680322`
107 |
108 | 
109 |
110 | There are many other ways to query by geo-location with the Algolia integration. The following input parameters also work with Scaphold:
111 |
112 | Parameter | Description
113 | ------------ | ------------
114 | `aroundLatLng` | Search for entries around a given location.
115 | `aroundRadius` | Maximum radius for geo search (in meters).
116 | `aroundPrecision` | Precision of geo search (in meters).
117 | `minimumAroundRadius` | Minimum radius (in meters) used for a geo search when aroundRadius is not set.
118 | `insideBoundingBox` | Search inside a rectangular area (in geo coordinates).
119 | `insidePolygon` | Search inside a polygon (in geo coordinates).
120 |
121 | !!! success ""
122 |
123 | You can read more about how Algolia's geo-search works here:
124 |
125 | - [Algolia Native SDK Example](https://www.algolia.com/doc/guides/geo-search/geo-search-overview/)
126 |
127 | - [Geo-Search Inputs](https://www.algolia.com/doc/api-client/javascript/search/#geo-search)
--------------------------------------------------------------------------------
/docs/integrations/social/login-with-auth0.md:
--------------------------------------------------------------------------------
1 | # Login using Auth0 Lock
2 |
3 | If you'd like to use Auth0 Lock as a client-side SDK to manage your authentication, you can do so with the `loginUserWithAuth0` mutation. This accepts the idToken returned from a successful Auth0 Lock login in the profile variable. These work in sync with all other Scaphold basic and social authentication flows so you can manage your users the way you want.
4 |
5 | !!! note "Example"
6 |
7 | ```graphql
8 | mutation Login($token:LoginUserWithAuth0Input!) {
9 | loginUserWithAuth0(input:$token) {
10 | user {
11 | id
12 | username
13 | createdAt
14 | }
15 | }
16 | }
17 |
18 | # Variables. Pass in the idToken returned by Auth0 Lock.
19 | {
20 | "token": {
21 | "idToken": ""
22 | }
23 | }
24 | ```
--------------------------------------------------------------------------------
/docs/integrations/social/login-with-native.md:
--------------------------------------------------------------------------------
1 | # Login using native SDKs
2 |
3 | ## Login
4 |
5 | If you are using a native social SDK such as the [Facebook SDK for React Native](https://github.com/facebook/react-native-fbsdk), use the SDK to generate an access token and then use the `loginUserWithAuth0Social` mutation to link the FB account with your scaphold user.
6 |
7 | After Scaphold verifies the access token with the OAuth provider (i.e. Facebook), we'll pass back the **JSON Web Token (JWT)** that you can add to your authorization header for future requests.
8 | Scaphold looks for a token under the `Authorization` key with the format `Bearer ` in your request headers and uses that to authenticate the user. It looks like this:
9 |
10 | 
11 |
12 | !!! note "Example"
13 |
14 | ```graphql
15 | # The loginWithAuth0Social mutation is a helper that you can use directly with native social SDKs.
16 | # I.E. you do not need to use an Auth0 SDK to generate an idToken.
17 | # You can also use an Auth0 SDK and the `loginWithAuth0` mutation but this requires one more step on your side.
18 |
19 | mutation Login($token:LoginUserWithAuth0SocialInput!) {
20 | loginUserWithAuth0Social(input:$token) {
21 | token
22 | user {
23 | id
24 | username
25 | }
26 | }
27 | }
28 | ```
29 |
30 | !!! warning
31 |
32 | The preferred way of logging in with Auth0 Lock is to use the `loginWithAuth0` mutation. For backwards compatibility,
33 | the `loginWithAuth0Lock` mutation will remain but `loginWithAuth0` is easier to use.
34 |
35 | !!! warning
36 |
37 | To use `loginWithAuth0Lock` make sure you have installed Lock version `^10.9.1`. Auth0 introduced
38 | a few non-backwards compatible changes to their API in the beginning of 2017. It must have been
39 | a new years resolution..
40 |
41 | ## Account Linking
42 |
43 | In addition, if you're logged in and make a request to any of the `loginUserWithAuth0` mutations but with a different social provider (i.e. Google),
44 | Scaphold will link your two accounts together. Now, you'll have access to both Facebook and Google information using Facebook's and Google's account credentials.
45 |
46 | 
47 |
48 | !!! warning "Update"
49 |
50 | Mutation name is now called `loginUserWithAuthOSocial`.
--------------------------------------------------------------------------------
/docs/integrations/social/setup.md:
--------------------------------------------------------------------------------
1 | # Social Auth
2 |
3 | Use **Auth0** to extend your app's login and registration flow painlessly through your favorite social authentication services.
4 |
5 | !!! tip "Tutorial"
6 |
7 | Get started with this [quick guide](https://scaphold.io/community/blog/social-auth-graphql/).
8 |
9 | ### Setup
10 |
11 | 1. Create a free [Auth0](https://auth0.com/) account. This will help you manage your app credentials like client IDs and secrets for your OAuth providers. By connecting your apps on your social accounts like Facebook, Google, and Twitter, you'll then have the correct account credentials to utilize these services for your authentication flow.
12 |
13 |
14 |
15 | 2. Configure Auth0 in Scaphold from the Integrations Portal to include the OAuth providers that you plan to use for your app. This will enable 3 new mutations `loginUserWithAuth0`, `loginWithAuth0Lock`, and `loginWithAuth0Social` that you can use to authenticate users.
16 |
17 | !!! warning ""
18 |
19 | If you are using Auth0 Lock then use `loginWithAuth0`, not `loginWithAuth0Lock`. `loginWithAuth0` is a simpler workflow and `loginWithAuth0Lock` only remains for backwards compatibility and will be deprecated.
20 |
21 | !!! note "Example"
22 |
23 | ```graphql
24 | # Use Auth0 Lock or native SDKs and an Auth0 client SDK to create an idToken.
25 | # Then use the loginWithAuth0 mutation to create/associate social tokens with scaphold users.
26 |
27 | mutation Login($token:LoginUserWithAuth0Input!) {
28 | loginUserWithAuth0(input:$token) {
29 | user {
30 | id
31 | username
32 | createdAt
33 | }
34 | }
35 | }
36 |
37 | # Variables
38 | {
39 | "token": {
40 | "idToken": ""
41 | }
42 | }
43 | ```
--------------------------------------------------------------------------------
/docs/persisted-queries/getting-started.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | ## Setup
4 |
5 | The first step is to create the mapping from our graphql queries to their ids. To do this we
6 | will use the `persistgraphql` npm package. `persistgraphql` expects that you store your
7 | queries as `.graphql` files in your source. If you aren't already doing this, the first step
8 | is to move your queries out to their own `.graphql` files and then `import` those queries from your
9 | components.
10 |
11 | Once you have your queries separated out into their own files then run these commands:
12 |
13 | 1. `npm install -g persistgraphql`
14 |
15 | 2. `persistgraphql < projectdir >`
16 |
17 | 3. This will generate an `extracted_queries.json` file that you will upload to Scaphold.
18 |
19 | !!! note
20 |
21 | You can also point `persistgraphql` to a single `.graphql` file. Pointing it to a directory will
22 | recursively pull queries out of every `.graphql` file.
23 |
24 | ## Upload Your Queries to Scaphold
25 |
26 | Uploading your `extracted_queries.json` file to Scaphold is easy. Go to the GraphiQL tab in your
27 | [Scaphold](https://scaphold.io) portal and click **Persisted Queries** in the page header. This
28 | will open a side panel where you can drop your `extracted_queries.json` file.
29 | We will automatically store this mapping so our servers know what query to call for a
30 | particular query id.
31 |
32 |
--------------------------------------------------------------------------------
/docs/persisted-queries/index.md:
--------------------------------------------------------------------------------
1 | # Persisted Queries
2 |
3 | ## Introduction
4 |
5 | Persisted queries let you store queries on our servers so that your app doesn't have to send
6 | the same query in each request. The immediate benefits of this are query whitelisting
7 | and reduced bandwidth usage. Mobile devices with limited connectivity benefit even more
8 | from persisted queries.
9 |
10 | !!! tip "Example"
11 |
12 | Difference between a traditional GraphQL request and one with a persisted query.
13 |
14 | Traditional GraphQL request:
15 |
16 | ```javascript
17 | {
18 | "query": "query getUser($id: ID!) { getUser(id: $id) { id username } }",
19 | "variables": { "id": 123 }
20 | }
21 | ```
22 |
23 | GraphQL request with persisted query:
24 |
25 | ```javascript
26 | {
27 | "id": 1,
28 | "variables": { "id": 123 }
29 | }
30 | ```
31 |
32 | ## How Persisted Queries Work
33 |
34 | A traditional GraphQL request includes a request body that looks like the one above. This
35 | example is relatively lightweight, but you can imagine that queries can get large quickly. When
36 | you are building large applications, that bandwidth usage can add up and reducing it can
37 | have very real impacts on your application's performance.
38 | The goal of persisted queries is to transform the first query into the second query.
39 |
40 | To make this work we need to create a mapping between the `id` `1` and the query
41 | `query getUser($id: ID!) { getUser(id: $id) { id username } }`.
42 |
43 | Mappings have the form:
44 |
45 | `{ [graphql-query]: < ID > }`
46 |
47 | For example, the mapping for our query to the right would look like this:
48 |
49 | `{ "query getUser($id: ID!) { getUser(id: $id) { id username } }": 1 }`
50 |
51 | Once you create the mapping,
52 | you can upload it to your server and then make requests using the query id instead of the full
53 | query text. Scaphold makes it easy for your create and upload this mapping so you can
54 | start reaping the benefits of persisted queries.
--------------------------------------------------------------------------------
/docs/persisted-queries/querying-from-client.md:
--------------------------------------------------------------------------------
1 | # Add Persisted Queries To Apollo Client
2 |
3 | Apollo Client comes with built in support for persisted queries. To enable it replace your
4 | network interface with one that looks like this. This will automatically replace your query text
5 | with the associated query id without any changes to your code. Scaphold will handle replacing
6 | the id with the query text on the server.
7 |
8 | ```javascript
9 | import queryMap from ‘./extracted_queries.json’;
10 | import { PersistedQueryNetworkInterface } from ‘persistgraphql’;
11 | const networkInterface = new PersistedQueryNetworkInterface({
12 | queryMap,
13 | uri: apiUrl,
14 | opts: {
15 | headers: {
16 | Authorization: < scaphold auth token >
17 | },
18 | },
19 | });
20 | const client = new ApolloClient({
21 | networkInterface,
22 | });
23 | ```
24 |
25 | # Using Persisted Queries Without Apollo Client
26 |
27 | Apollo Client makes it easy to use persisted queries but you're welcome to manage the queries
28 | yourself. All you need to do is replace the `"query"` property in each of your requests with
29 | the associated `"id"`. Keep in mind though, that it's up to you to make sure that you have updated
30 | the most recent version of your queries to our servers.
31 |
32 | !!! tip ""
33 |
34 | See the [How Persisted Queries Work](/persisted-queries/#how-persisted-queries-work) section for an example request body.
--------------------------------------------------------------------------------
/docs/realtime/slackr-chat-app.md:
--------------------------------------------------------------------------------
1 | # Build Slack Tutorial
2 |
3 | 
4 |
5 | ## The Full Slackr Tutorial
6 |
7 | Follow along as we build a real-time chat application called Slackr. We will cover subscriptions in depth
8 | and show you how to quickly make real-time apps with Apollo Client. It covers a lot of material
9 | and has a full example app to boot!
10 |
11 | !!! tip "Tutorial"
12 |
13 | [How to Build real-time Apps with GraphQL Subscriptions](/tutorials/realtime-apps-with-subscriptions/)
14 |
15 | !!! tip "Slackr Starter Kit"
16 |
17 | Check out the [Slackr Starter Kit on Github](https://github.com/scaphold-io/slackr-graphql-subscriptions-starter-kit)
18 |
19 | Use it to jump start your next real-time application!
--------------------------------------------------------------------------------
/docs/realtime/subscriptions-apollo.md:
--------------------------------------------------------------------------------
1 | ## Subscriptions With Apollo Client
2 |
3 | Apollo Client is a GraphQL networking interface that provides a fully-featured
4 | GraphQL caching client for any server or UI framework. It's an easy-to-use GraphQL networking client
5 | that works with HTTP and web socket requests. We use it at Scaphold to power our web apps, and many
6 | of the examples in [our GitHub](https://github.com/scaphold-io). But you can also use it for your
7 | React Native apps as well to address mobile needs. They are coming out with support for native mobile
8 | iOS and Android clients as well. You can read more about it [here](http://dev.apollodata.com/).
9 |
10 | The Scaphold GraphiQL page has already implemented the subscription protocol for you. The good news is that it is really easy to set this up in your own application. Here is how.
11 |
12 | 1. **Download Apollo Client from npm!** (Apollo Client works pretty much the same whether you are building a React, AngularJS, or vanilla JavaScript applications)
13 |
14 | - `npm install apollo-client graphql-tag --save`
15 |
16 | - If using React also `npm install react-apollo --save`
17 |
18 | - If using Angular2 also `npm install angular2-apollo --save`
19 |
20 | 2. **Configure the Apollo Client network layer to work with websockets.** To do this we can use the following two code snippets:
21 |
22 | - The `addGraphQLSubscriptions` function retrofits the Apollo Client network interface with the subscribe and unsubscribe methods that we can use from our application code.
23 |
24 | - The `makeApolloClient` function then creates a new Apollo Client instance, applies the subscription methods, and adds a peice of authentication middleware before returning the client for use in our application.
25 |
26 | This is all we need to do to configure our Apollo Client instance for GraphQL Subscriptions.
27 |
28 | !!! tip ""
29 |
30 | Condensed example from our Slackr app
31 |
32 | ```javascript
33 | /* File: addGraphQLSubscriptions.js */
34 |
35 | import { print } from 'graphql-tag/printer';
36 |
37 | // quick way to add the subscribe and unsubscribe functions to the network interface
38 | export default function addGraphQLSubscriptions(networkInterface, wsClient) {
39 | return Object.assign(networkInterface, {
40 | subscribe(request, handler) {
41 | return wsClient.subscribe({
42 | query: print(request.query),
43 | variables: request.variables,
44 | }, handler);
45 | },
46 | unsubscribe(id) {
47 | wsClient.unsubscribe(id);
48 | },
49 | });
50 | }
51 |
52 | /* End of file: addGraphQLSubscriptions.js */
53 |
54 | ------------------------------------------------------------------------
55 |
56 | /* File: makeApolloClient.js */
57 |
58 | import addGraphQLSubscriptions from './addGraphQLSubscriptions';
59 | import ApolloClient, { createNetworkInterface } from 'apollo-client';
60 | import { Client } from 'subscriptions-transport-ws';
61 |
62 | // creates a subscription ready Apollo Client instance
63 | export function makeApolloClient() {
64 | const scapholdUrl = 'us-west-2.api.scaphold.io/graphql/scaphold-graphql';
65 | const graphqlUrl = `https://${scapholdUrl}`;
66 | const websocketUrl = `wss://${scapholdUrl}`;
67 | const networkInterface = createNetworkInterface(graphqlUrl);
68 | networkInterface.use([{
69 | applyMiddleware(req, next) {
70 | // Easy way to add authorization headers for every request
71 | if (!req.options.headers) {
72 | req.options.headers = {}; // Create the header object if needed.
73 | }
74 | if (localStorage.getItem('scaphold_user_token')) {
75 | // This is how to authorize users using http auth headers
76 | req.options.headers.Authorization = `Bearer ${localStorage.getItem('scaphold_user_token')}`;
77 | }
78 | next();
79 | },
80 | }]);
81 | const wsClient = new Client(websocketUrl);
82 | const networkInterfaceWithSubscriptions = addGraphQLSubscriptions(networkInterface, wsClient);
83 |
84 | const clientGraphql = new ApolloClient({
85 | networkInterface: networkInterfaceWithSubscriptions,
86 | initialState: {},
87 | });
88 | return clientGraphql;
89 | }
90 |
91 | /* End of File: makeApolloClient.js */
92 | ```
--------------------------------------------------------------------------------
/docs/realtime/subscriptions-react.md:
--------------------------------------------------------------------------------
1 | ## Subscriptions With React
2 |
3 | Let's look at a more real world example using React. Apollo comes packed with really nice React bindings
4 | that we can use to simplify the process of subscribing to data and merging new data into the client-side cache.
5 |
6 | Take a minute to look over this file. Apollo does a great job allowing us to use real-time subscriptions alongside traditional queries and mutations. The combination of `subscribeToMore` and `updateQuery` offer a powerful set of tools to keep our UI up to date when dealing with real-time data!
7 |
8 | !!! tip ""
9 |
10 | [See the complete code on GitHub](https://github.com/scaphold-io/slackr-graphql-subscriptions-starter-kit/blob/master/src/components/messages.jsx)
11 |
12 | The `returnPartialData: true` option is important. When you want to use `subscribeToMore` to merge results into the result of a normal query it is necessary to specify this.
13 |
14 | A few things are going on here. To make sense of what is happening, lets start from the logical beginning which actually occurs at the end of the file. See this line `const MessagesWithData = compose(graphql(ChannelMessagesQuery, ...));`. This is the standard way to use Apollo to connect a react component with data from a GraphQL query. The `graphql` function will wrap our component in a higher-order component that grabs our data and makes it available to our component via its `props`. In this example, we will be able to access our `Channel` data from our component with `this.props.data.getChannel` as soon as it is fetched.
15 |
16 | Okay so we have connected our component with a regular old GraphQL query, but how do we make it real-time? The key is the method `subscribeToMore`. Look at our `subscribeToNewMessages` method. Apollo's `graphql` function fits our component with the data prop that exposes the `subscribeToMore` method. We use this method to attach a subscription query which then calls the `updateQuery` method we pass in every time a new peice of data is pushed from the server. The object we return from `updateQuery` is then merged with our previous results and persisted in the client side cache. This way, `this.props.data.getChannel...` is always kept up to date and we can use it like normal to render our UI.
17 |
18 | !!! tip ""
19 |
20 | Condensed example from our Slackr app
21 |
22 | ```javascript
23 | import React from 'react';
24 | import { graphql, compose } from 'react-apollo';
25 | import gql from 'graphql-tag';
26 |
27 | const ChannelMessagesQuery = gql`
28 | query GetPublicChannels($channelId: ID!, $messageOrder: [MessageOrderByArgs]) {
29 | getChannel(id: $channelId) {
30 | id
31 | name
32 | messages(last: 50, orderBy: $messageOrder) {
33 | edges {
34 | node {
35 | id
36 | content
37 | createdAt
38 | author {
39 | id
40 | username
41 | nickname
42 | picture
43 | }
44 | }
45 | }
46 | }
47 | }
48 | }
49 | `;
50 |
51 | class Messages extends React.Component {
52 |
53 | ...
54 |
55 | componentWillReceiveProps(newProps) {
56 | if (
57 | !newProps.data.loading &&
58 | newProps.data.getChannel
59 | ) {
60 | if (
61 | !this.props.data.getChannel ||
62 | newProps.data.getChannel.id !== this.props.data.getChannel.id
63 | ) {
64 | // If we change channels, subscribe to the new channel
65 | this.subscribeToNewMessages();
66 | }
67 | }
68 | }
69 |
70 | /*
71 | * Initiates the subscription and specifies how new data should be merged
72 | * into the cache using the updateQuery method.
73 | */
74 | subscribeToNewMessages() {
75 | this.subscription = this.props.data.subscribeToMore({
76 | document: gql`
77 | subscription newMessages($subscriptionFilter:MessageSubscriptionFilter) {
78 | subscribeToMessage(mutations:[createMessage], filter: $subscriptionFilter) {
79 | value {
80 | id
81 | content
82 | createdAt
83 | author {
84 | id
85 | username
86 | nickname
87 | picture
88 | }
89 | }
90 | }
91 | }
92 | `,
93 | variables: {
94 | subscriptionFilter: {
95 | channelId: {
96 | // We're using react-router and grabbing the channelId from the url
97 | // to designate which channel to subscribe to
98 | eq: this.props.params ? this.props.params.channelId : null
99 | }
100 | }
101 | },
102 |
103 | /*
104 | * Update query specifies how the new data should be merged
105 | * with our previous results. Note how the structure of the
106 | * object we return here directly matches the structure of
107 | * the GetPublicChannels query.
108 | */
109 | updateQuery: (prev, { subscriptionData }) => {
110 | const newEdges = [
111 | ...prev.getChannel.messages.edges,
112 | {
113 | node: {
114 | ...subscriptionData.data.subscribeToMessage.value,
115 | }
116 | }
117 | ];
118 | return {
119 | getChannel: {
120 | messages: {
121 | edges: newEdges,
122 | }
123 | }
124 | };
125 | },
126 | });
127 | }
128 |
129 | ...
130 | }
131 |
132 | const MessagesWithData = compose(
133 | graphql(ChannelMessagesQuery, {
134 | options: (props) => {
135 | const channelId = props.params ? props.params.channelId : null;
136 | return {
137 | returnPartialData: true,
138 | variables: {
139 | channelId,
140 | messageOrder: [
141 | {
142 | field: 'createdAt',
143 | direction: 'ASC'
144 | }
145 | ],
146 | },
147 | };
148 | },
149 | }),
150 | ... // We compose a few more queries in the actual app.
151 | )(Messages);
152 |
153 | export default MessagesWithData;
154 | ```
--------------------------------------------------------------------------------
/docs/search/fulltext-search.md:
--------------------------------------------------------------------------------
1 | Many apps are search-based, meaning that they'll need to look up entries in a database to find relevant search results.
2 | However, this is not an easy task and requires a particular type of indexing for the it work fast and provide a fluid
3 | user experience.
4 |
5 | !!! tip "Learn More"
6 |
7 | Scaphold manages search indexes via an integration called Algolia. It's free to sign up, and you can [learn more here](/integrations/search/#full-text-search).
--------------------------------------------------------------------------------
/docs/search/geo-search.md:
--------------------------------------------------------------------------------
1 | Many apps nowadays need location-based features in order for them to provide wonderful experiences to their users.
2 | Scaphold allows you to query for data by geo-location for location-based apps.
3 | Scaphold uses an integration called Algolia in order to do so. They help manage search indexes
4 | so that you can perform queries for geo-based queries.
5 |
6 | !!! tip "Learn More"
7 |
8 | To learn more about how to set them up and how they work, head over to the [integrations section of the docs](/integrations/search/#geo-search).
--------------------------------------------------------------------------------
/docs/starter-kits.md:
--------------------------------------------------------------------------------
1 | # Starter Kits
2 |
3 | Getting started building a new app can be a daunting task. The development world moves at lightning
4 | speed but don't worry! We work hard to make it as simple as possible to start by maintaining
5 | a bunch of starter kits and our community builds a lot more.
6 |
7 | Pick the stack and platform that works for you. We have already built the scaffolding to get you started in seconds.
8 |
9 |
29 | React is the hot topic in web development. It is a component based framework specifically designed to create powerful & performant user interfaces. It is supported by Facebook and is backed by a huge community. It also fits perfectly with GraphQL.
30 |
51 | Angular is a web and mobile development framework that came out of Google. It promotes bi-directional
52 | data binding and is great for building real-time apps. Angular2 is brand new and ready for you
53 | to start building on.
54 |
72 | Vue 2 is a *progressive* Javascript view framework that is designed to have a light footprint and be incrementally adoptable. Vue 2 combines templating (like Angular) with a reactive Virtual DOM rendering engine based on VDOM for amazing rendering performance (x2 React).
73 |
94 | React Native is the new way to build mobile apps. You write your code once with
95 | Javascript and your app is automatically converted to native code and deployed so you can create rich experiences on any platform.
96 |
114 | The GraphQL type system fits in perfectly with your swift projects. Its magic watching xcode
115 | autocomplete and type check your GraphQL queries. Apollo iOS is a great place to start.
116 |
168 |
169 | !!! note ""
170 |
171 | **Like to contribute?**
172 |
173 | - Submit a PR [here](https://github.com/scaphold-io/scaphold-docs/edit/master/docs/getting-started/starter-kits.md).
174 | - Send us your starter kit by emailing [hi@scaphold.io](mailto:hi@scaphold.io).
--------------------------------------------------------------------------------
/docs/tutorials/adding-social-auth.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Social Authentication with OAuth + GraphQL
3 | date: 2016-06-28 14:23:37 -0700
4 | categories:
5 | description: "Social authentication has been a hot topic for developers and users alike. There are many guides on how to implement it for traditional REST-based APIs, so here’s one for the good guys."
6 | photo: "https://assets.scaphold.io/community/blog/social-auth-graphql/social-auth-hero.png"
7 | headshot: "https://assets.scaphold.io/images/vince.jpg"
8 | author: "Vince Ning"
9 | ---
10 |
11 | ## How to integrate OAuth with [Scaphold's](https://scaphold.io) GraphQL platform.
12 |
13 | 
14 |
15 | Hi GraphQL-Lovers!
16 |
17 | Social authentication has been a hot topic for developers and users alike. There are many guides on how to implement it for traditional REST-based APIs, so here's one for the good guys.
18 |
19 | Why social authentication? It all starts with the users. Most people have online social accounts on Facebook, Twitter, Google, and more. So they're thinking,
20 | > “I'll check out your app if I can just log in with my existing Facebook or Twitter account.”
21 |
22 | The benefits are two-fold.
23 |
24 | 1. *Users* are excited to get to remember one less account name and password. Hooray!
25 |
26 | 1. *Developers* gets more content from these social authentication providers immediately upon log in, like emails, profile pictures, friends, etc…
27 |
28 | Here's how it works at a high level:
29 |
30 | ](https://assets.scaphold.io/community/blog/social-auth-graphql/oauth-flow.png)
31 |
32 | *Source: [https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2)*
33 |
34 | 1. User must grant the application permission to access secured resources from an OAuth provider (i.e. Facebook, Google, etc).
35 |
36 | 1. After the user successfully grants the application access, the OAuth provider generates a user access token and passes it back to the application.
37 |
38 | 1. The application sends this user access token to the OAuth provider along with its own identity in order to authenticate.
39 |
40 | 1. Upon successful authentication, the OAuth provider grants authorization and returns an app access token.
41 |
42 | 1. Now that authorization is completed, the application may use the app access token for authentication to fetch authorized resources from the OAuth provider.
43 |
44 | 1. App access token is verified and the requested OAuth provider's resources are sent back to the application.
45 | > #This isn't easy!
46 |
47 | To do this yourself, you will have to perform the same workflow for every OAuth provider you wish to authenticate with. The best tool that we've found to help manage all these connections is [Auth0](https://auth0.com). We'll be using this tool for the GraphQL walkthrough to help us set up OAuth.
48 |
49 | Today, we'll explore two of the most popular social providers:
50 | > **Facebook** and **Google**.
51 |
52 | Here's the agenda:
53 |
54 | 1. Create an Auth0 account.
55 |
56 | 1. Configure Facebook and Google connections.
57 |
58 | 1. Use Auth0 account keys to configure an Auth0 integration on [Scaphold](https://scaphold.io).
59 |
60 | 1. Obtain access token from Facebook client SDK, exchange it for an ID token, and send GraphQL request to authenticate.
61 |
62 | 1. Store JSON Web Token (JWT) as Authorization header in HTTP requests.
63 |
64 | 1. Once logged in, send another GraphQL request with a Google ID token to link both social authentication credentials.
65 |
66 | 1. Voilá!
67 |
68 | **Let's start by [creating a free Auth0 account](https://auth0.com/).**
69 |
70 | This will help you manage your app credentials like client IDs and secrets for your OAuth providers. By connecting your apps on your social accounts like Facebook, Google, and Twitter, you'll then have the correct account credentials to utilize these services for your authentication flow.
71 |
72 | 
73 |
74 | For more information on configuring your social connections on Auth0, check out these guides: [Facebook](https://auth0.com/docs/connections/social/facebook) / [Google](https://auth0.com/docs/connections/social/google)
75 |
76 | Once you've tested out your connections to see that they work, **save your Auth0 Domain, Client ID, and Client Secret**.
77 |
78 | 
79 |
80 | Next, **configure Auth0 in [Scaphold](https://scaphold.io)** from the Integrations Portal to include the OAuth providers that you plan to use for your app. This will enable a new mutation in your schema called **loginUserWithAuth0** which you can use to log in users with connected OAuth providers.
81 |
82 | 
83 |
84 | In your client app, you'll likely be using a client SDK to handle user login. For instance, you can use the [Facebook SDK for React Native](https://github.com/facebook/react-native-fbsdk) to ask your users to log in with their existing Facebook accounts. Your app will redirect them to a Facebook sign-in page and once this succeeds, you'll receive an **access token** that you'll verify with your OAuth provider, and then the corresponding `idToken` will be sent to [Scaphold](https://scaphold.io).
85 |
86 | const FBSDK = require('react-native-fbsdk');
87 | const rp = require('request-promise');
88 |
89 | const {
90 | LoginButton,
91 | AccessToken
92 | } = FBSDK;
93 |
94 | var Login = React.createClass({
95 | render: function() {
96 | return (
97 |
98 | {
102 | if (error) {
103 | alert("login has error: " + result.error);
104 | } else if (result.isCancelled) {
105 | alert("login is cancelled.");
106 | } else {
107 | AccessToken.getCurrentAccessToken().then(
108 | (data) => {
109 |
110 | /*
111 | * Use Access Token to retrieve Auth0 ID Token
112 | */
113 |
114 | const options = {
115 | method: 'POST',
116 | uri: url,
117 | body: {
118 | client_id: 'xxxxxxxxxxx', // Your Auth0 Client ID
119 | access_token: data.accessToken.toString(),
120 | connection: 'facebook',
121 | scope: 'openid'
122 | },
123 | json: true,
124 | resolveWithFullResponse: true
125 | };
126 | return rp(options);
127 | }).then(res => {
128 |
129 | /*
130 | * Example response
131 | * {
132 | * "idToken": "eyJ0eXAiOiJKV1Qi...",
133 | * "access_token": "A9CvPwFojaBI...",
134 | * "token_type": "bearer"
135 | * }
136 | */
137 |
138 | alert(res.body.idToken);
139 |
140 | /*
141 | * Send Scaphold login mutation (shown below)
142 | */
143 |
144 | }).catch(err => {
145 | logger.error(err);
146 | throw httpError(403, err);
147 | });
148 | )
149 | }
150 | }
151 | }
152 | onLogoutFinished={() => alert("logout.")}/>
153 |
154 | );
155 | }
156 | });
157 |
158 | Given the `idToken`, here's the **GraphQL mutation you can use to log your user in** to [Scaphold](https://scaphold.io):
159 |
160 | // GraphQL Mutation
161 |
162 | mutation LoginUserWithAuth0 ($input: LoginUserWithAuth0Input!) {
163 | loginUserWithAuth0 (input: $input) {
164 | user {
165 | id
166 | username
167 | createdAt
168 | }
169 | }
170 | }
171 |
172 | // Variables
173 |
174 | {
175 | "input": {
176 | "idToken": "eyJ0eXAiOiJKV1Qi..." // idToken from above
177 | }
178 | }
179 |
180 | Once you've sent that request, the response should resemble this:
181 |
182 | 
183 |
184 | After [Scaphold](https://scaphold.io) securely logs the user in with the `idToken`, we'll pass back the newly generated `idToken` that you'll need to **add to your Authorization header** for future requests.
185 | That way, Scaphold will be able to authenticate you to make future requests through Scaphold, and it will also provide us the capability to work on that user's behalf to access the OAuth provider's resources.
186 | In this case, we could authorize you to access their Facebook friends and their public profile information.
187 |
188 | 
189 |
190 | In addition, if you've logged in already and you make the same request again but with a new OAuth provider (i.e. Google), Scaphold will **link your two accounts together**, since we know the requests being made belong to the same user.
191 |
192 | In a similar fashion to the Facebook work flow from earlier, you'll likely use a [client SDK for Google sign-in](https://github.com/devfd/react-native-google-signin). Upon logging in, you'll receive an access token and again verify it with the OAuth provider like so:
193 |
194 | GoogleSignin.signIn().then((user) => {
195 | /*
196 | * Send Access Token to Scaphold (shown below)
197 | */
198 | const options = {
199 | method: 'POST',
200 | uri: url,
201 | body: {
202 | client_id: 'xxxxxxxxxxx', // Your Auth0 Client ID
203 | access_token: user.accessToken,
204 | connection: "google_oauth2",
205 | scope: 'openid'
206 | },
207 | json: true,
208 | resolveWithFullResponse: true
209 | };
210 | return rp(options);
211 | }).then(res => {
212 | alert(res.body.idToken);
213 |
214 | /*
215 | * Send Scaphold login mutation (like earlier above)
216 | */
217 |
218 | }).catch((err) => {
219 | console.log('WRONG SIGNIN', err);
220 | }).done();
221 |
222 | With this `idToken`, you'll **send a GraphQL request to Scaphold the same way as earlier**.
223 |
224 | Congratulations! Now, you'll have access to both Facebook and Google information using your users' Facebook and Google account credentials. Thanks for checking this out, and check out our [community page](https://scaphold.io/community) for more tutorials on simple ways to get started with GraphQL!
225 |
226 |
230 |
--------------------------------------------------------------------------------
/docs/tutorials/aggregations-in-graphql.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Build Data Driven Applications with GraphQL Aggregations
3 | date: 2016-01-15 00:01:37 -0700
4 | categories: GraphQL, SQL, Analytics, Aggregations
5 | description: "A quick crash course on how to start building powerful data-driven applications with GraphQL aggregations"
6 | photo: "https://assets.scaphold.io/community/blog/aggregations-in-graphql/BigData.jpg"
7 | headshot: "https://assets.scaphold.io/images/michael.jpg"
8 | author: "Michael Paris"
9 | ---
10 |
11 | # Build Data Driven Applications with GraphQL Aggregations
12 |
13 | Today we launched our newest feature with Aggregations! All applications now come with
14 | powerful analytical capabilities by default!
15 | Go take a look at the documentation explorer in the GraphiQL tab of the Scaphold portal to check out
16 | all the awesome new functionality. In the meantime, here are the basics!
17 |
18 | ## Aggregations crash course
19 |
20 | Imagine you were building a blogging platform like Medium. It would be an obvious value add
21 | to be able to slice & dice your data to better understand how your posts are performing. Let's
22 | say our blogging platform has an `Article` model like this:
23 |
24 | ```graphql
25 | type Article {
26 | id: ID!
27 | title: String
28 | content: Text # Like a String but larger
29 | recommends: Int
30 | reads: Int
31 | }
32 | ```
33 |
34 | It would be really nice to be able to ask questions like, how many times were all our articles
35 | read in 2016?
36 | With aggregations and GraphQL this is easy!
37 |
38 | ```graphql
39 | query LikeData {
40 | viewer {
41 | allArticles(where:{ createdAt: { gt: "2016", lt: "2017" }}) {
42 | aggregations {
43 | sum {
44 | reads
45 | }
46 | }
47 | }
48 | }
49 | }
50 | ```
51 |
52 | This is great, but what if we also wanted to know the average read count and the total number
53 | of articles listed on the site.
54 | While we're at it, lets also grab the total & average number of recommendations for our articles.
55 |
56 | ```graphql
57 | query PostData {
58 | viewer {
59 | allArticles(where:{ createdAt: { gt: "2016", lt: "2017" }}) {
60 | aggregations {
61 | count
62 | sum {
63 | recommends
64 | reads
65 | }
66 | avg {
67 | recommends
68 | reads
69 | }
70 | }
71 | }
72 | }
73 | }
74 | ```
75 |
76 | This query is both intuitive to understand and performant. Behind the scenes all of these
77 | metrics are calculated in a single query keeping your api snappy and you user's happy! Aggregations
78 | open the door for all kinds of insights into our data. For example, we could use the query above
79 | to answer a question like what is the average conversion rate from a read to a recommendation.
80 |
81 | This is just touching the surface of our aggregation capabilities. Let's keep digging.
82 |
83 | ## Advanced Aggregations Using Connections
84 |
85 | In the previous section, we showed how you can crunch the numbers for all the records
86 | in your dataset. This is useful for administrative tasks, but often our applications
87 | are more user-centric. The good news is that aggregations work for every connection in
88 | your API.
89 |
90 | Let's compound on the previous example and ask the question, "How many reads did MY posts
91 | get on average in 2016?". To make this work, we need to introduce a `User` type.
92 |
93 | ```graphql
94 | type User {
95 | id: ID!
96 | username: String
97 | password: Secret
98 | articles: ArticleConnection
99 | }
100 |
101 | type Article {
102 | id: ID!
103 | title: String
104 | content: Text
105 | recommends: Int
106 | reads: Int
107 | author: User
108 | }
109 | ```
110 |
111 | Once we setup the connection between our `User` and `Article` types, we can issue a query like this
112 |
113 | ```graphql
114 | query PostData {
115 | viewer {
116 | user {
117 | articles(where:{ createdAt: { gt: "2016", lt: "2017" }}) {
118 | aggregations {
119 | count
120 | sum {
121 | recommends
122 | reads
123 | }
124 | avg {
125 | recommends
126 | reads
127 | }
128 | }
129 | }
130 | }
131 | }
132 | }
133 | ```
134 |
135 | This query will only perform the aggregation on the articles that are attached to the logged in
136 | user via the `articles` connection. You can continue to manipulate these queries with more and more
137 | advanced filters and aggregations. You'll find that every connection in your API operates the same
138 | way allowing you do run multiple aggregations at different levels in your GraphQL API!
139 |
140 | ## Thanks for reading
141 |
142 | I hope this helps show you the kinds of queries you can run to build powerful, data-driven
143 | applications with GraphQL! Thanks for reading! Please let me know what you think in the comments!
144 |
145 | Happy Scapholding!
146 |
147 | [Join us on slack](http://slack.scaphold.io)
--------------------------------------------------------------------------------
/docs/tutorials/android-push-notifications.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Android Push Notifications with GraphQL
3 | date: 2016-02-11 00:00:00 -0700
4 | categories: Scaphold, Android, Push Notifications, GCM
5 | description: "Using GraphQL to enable Android GCM push notifications for mobile devices in 4 easy steps."
6 | photo: "https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/banner_gcm_push.png"
7 | headshot: "https://assets.scaphold.io/images/vince.jpg"
8 | author: "Vince Ning"
9 | ---
10 |
11 | ## Step-by-step guide on setting up Apple push notifications for your GraphQL backend
12 |
13 | 
14 |
15 | Calling all Android mobile developers! Today, we’re going to walk you through how to set up Android push notifications for your GraphQL server.
16 |
17 | > It’s about time for mobile to get some GraphQL love. ❤️
18 |
19 | We’re going to cut right to the chase with the **3 steps** to get you set up right away with Android push notifications.
20 |
21 | > Prerequisite: You must have an actual Android device configured for development.
22 |
23 | # 1. Create a Google API project to enable GCM.
24 |
25 | We’re going to start off by creating an Android app. To make it easier for you, we’ve provided a starter kit that goes along with this guide.
26 |
27 | [**Download the starter kit here**](https://github.com/scaphold-io/android-gcm-push-graphql-starter-kit).
28 |
29 | Once we’ve created the app, we’ll want to connect it to a Google project. Please go to your [Google Developer Console](https://console.cloud.google.com/apis) and **create a new project**.
30 |
31 | 
32 |
33 | Once you’ve created your Google project, you’ll have to also create a new Firebase project at the [Firebase console](https://console.firebase.google.com/).
34 |
35 | 
36 |
37 | > Why do we have to go to Firebase? Google now only provides GCM push notification support through the Firebase console. 😤
38 |
39 |
40 |
41 |
42 |
43 | Follow the steps to finish creating your app, then **Add Firebase to your Android app**.
44 |
45 | 
46 |
47 | We’ll need your **Package Name (or Application ID)**. Find your package name here in your app:
48 |
49 | 
50 |
51 | The **nickname** is your choice. Your first step should look like this.
52 |
53 |
54 |
55 |
56 |
57 | After adding the app, you’ll download a Google Service JSON file that you’ll need to import into your project. Place it in your **app** directory.
58 |
59 |
60 |
61 |
62 |
63 | Great! Now you’ll need to update the following dependencies in your Android app.
64 |
65 | - App-level *bundle.gradle*:
66 |
67 | 
68 |
69 | - Project-level *bundle.gradle*:
70 |
71 | 
72 |
73 | And finally, you’ll need to update your strings.xml constants file to reflect the App ID of the newly created Google project.
74 |
75 | 
76 |
77 | > Nice work ✅ Your Android app and Google project are now connected!
78 |
79 | # 2. Integrate your GraphQL server with GCM.
80 |
81 | Now, it’s time to connect your GraphQL server with GCM so that you can actually send push notifications.
82 |
83 | Let’s first grab your **Server API Key** that was auto-generated by Google after successful completion of the previous process of connecting your Android app with your Google project. You’ll need to find it in your Google Developer Console.
84 |
85 | **Open up the left side panel** by clicking the top-left hamburger menu, and navigate to the **API Manager**.
86 |
87 | 
88 |
89 | Now click the **Credentials** tab on the left-hand menu to grab your **Server API Key**. Save this. You’ll need it in the next step to connect to your GraphQL server.
90 |
91 | 
92 |
93 | To make it easy, [Scaphold.io](https://scaphold.io) provides an easy way to set up your very own GraphQL server for free in minutes. [**Sign up for an account**](https://scaphold.io/?signupModal=true) and **create an app**. At this point, you instantly have a GraphQL server deployed in production, ready to make requests.
94 |
95 | Now, go to Scaphold’s Integrations Portal to enable Android push notifications for development. **Click Add** to begin.
96 |
97 | 
98 |
99 | A form will then pop up, asking for your **Server API Key**. Fill out the appropriate fields and **hit Create**.
100 |
101 |
102 |
103 |
104 |
105 | Good job! You’ve now connected your GraphQL server to be able to send push notifications to devices with your app installed.
106 |
107 | # 3. Obtain a device token to send push notifications.
108 |
109 | In order to send a push notification to a device, we need to generate a device token that is a unique identifier for a device and an app together. In that case, we’ll need to install your app onto an actual Android device.
110 |
111 | But first, we need to create an event handler to receive the device token once a user permits the app to send push notifications to their phone. In your app, you’ll need to put the following snippet in your ExternalReceiver file (i.e. class that extends BroadcastReceiver):
112 |
113 | ```javascript
114 | public class ExternalReceiver extends BroadcastReceiver {
115 | private void register() {
116 | new AsyncTask(){
117 | protected Object doInBackground(final Object... params) {
118 | String token;
119 | try {
120 | token = gcm.register(getString(R.string.project_number));
121 | Log.i("registrationId", token);
122 | }
123 | catch (IOException e) {
124 | Log.i("Registration Error", e.getMessage());
125 | }
126 | return true;
127 | }
128 | }.execute(null, null, null);
129 | }
130 | }
131 | ```
132 |
133 | We’re going to be using the [boilerplate code from earlier](https://github.com/scaphold-io/android-gcm-push-graphql-starter-kit) to illustrate.
134 |
135 | Now install your app on your Android device by pressing the big ▶️ button in the top bar of Android Studio.
136 |
137 | > Note: An actual device is needed since the simulator is transient and cannot have a device token associated to it.
138 |
139 | Upon success, your device token will be issued by GCM and printed out in your Android Studio console.
140 |
141 | 
142 |
143 | Save this device token and send it to Scaphold to register it on your behalf with GCM. This take the form of sending a GraphQL mutation from your app, but for demonstration purposes, we’ll use GraphiQL to do so.
144 |
145 | 
146 |
147 |
148 |
149 | Congratulations! You’ve successfully configured your app and device to be able to send and receive iOS push notifications. And the best part is that it was all done with GraphQL.
150 |
151 | Now that your GraphQL server is set up for iOS push notifications, the next part of this guide will walk you through how to...
152 |
153 | - Send push notifications
154 | - Associate users to device tokens
155 | - Manage push channels (i.e. groups)
156 |
157 | **Thanks for following along! We'll walk you through sending your first push notification with GraphQL in [the next part of this tutorial](/blog/send-push-notifications-scaphold-graphql/).**
158 |
159 | Looking for how to set up iOS push notifications with APNS? [Follow along here!](/blog/iOS-push-notifications-with-graphql/)
160 |
161 | Enjoy how easy that was to set up a GraphQL server? [**Join Scaphold today!**](https://scaphold.io) And be sure to follow us on [Twitter](https://twitter.com/ScapholdDotIO) or join our [Slack](http://slack.scaphold.io/) for more awesome ways to learn about how to quickly launch your next app with GraphQL!
--------------------------------------------------------------------------------
/docs/tutorials/apollo-optics-graphql-server.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Apollo Optics and your GraphQL Server
3 | date: 2016-02-10 12:00:00 -0700
4 | categories: Scaphold, Apollo Optics, Analytics
5 | description: "Performance monitoring for Scaphold apps."
6 | photo: "https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Apollo_Optics_Dashboard.png"
7 | headshot: "https://assets.scaphold.io/images/vince.jpg"
8 | author: "Vince Ning"
9 | ---
10 |
11 | Hi folks!
12 |
13 | Today, we’re going to be taking a deep dive into how you can analyze your GraphQL server for performance monitoring.
14 |
15 | ## **Why analytics is important?**
16 |
17 | ### *For those that care about performance, money, UX, and scalability.*
18 |
19 | As we move into a GraphQL-first world, it’s important to be able to understand not only what data you’re fetching, but also how it’s being fetched. Many people think of this as an after-thought, especially for those trying to “move fast and break things”, in hopes that getting as much code out as possible as fast as possible will help them win over their competition.
20 |
21 | However, “moving fast” can be costly on many levels. First of all, your application’s end users will feel the clunkiness of your application. In addition, on your servers, fetching data the wrong way can create inefficiencies that cost CPU usage, memory leaks, and slow response times, not to mention your AWS bill skyrocketing through the roof. Not the type of growth you’re looking for probably.
22 |
23 | > Real world example:
24 |
25 | In 2016, **Facebook reported to have lost $19,385 per minute** in profits that it was down. [http://bit.ly/2l6wibD](http://bit.ly/2l6wibD)
26 |
27 | ### *Performance matters.*
28 |
29 | ## **Let’s get started!**
30 |
31 | ### *Easily hook up Apollo Optics into your GraphQL API in a couple minutes.*
32 |
33 | There’s a simple-to-use tool called [Apollo Optics](http://www.apollodata.com/optics). You can try it out for free, and it comes with a beautiful dashboard that helps you visualize your queries to gain insights on request order and response times.
34 |
35 | 
36 |
37 | GraphQL lends itself to awesome tooling like this due to its type system, where you can clearly and automatically introspect what the inputs and outputs are of a an API.
38 |
39 | Here are the steps:
40 |
41 | 1. **Sign up for Scaphold and create an app.**
42 |
43 | Once you hit *Create*, you’ll automatically have a production-ready GraphQL server and API at your disposal.
44 |
45 | 
46 |
47 | 2. **Sign up for [Apollo Optics](http://www.apollodata.com/optics) and grab your API key.**
48 |
49 | On your Apollo Optics account, open your *app settings*.
50 |
51 | 
52 |
53 | *Copy your API key* for use in the next step.
54 |
55 | 
56 |
57 | 3. **Add Apollo Optics integration on Scaphold.**
58 |
59 | In the Integrations Dashboard, click *Add*.
60 |
61 | 
62 |
63 | *Paste your API key* from earlier into the appropriate field, then hit *Create*.
64 |
65 | 
66 |
67 | 4. **Make a request and see it in your Optics dashboard!**
68 |
69 | In this case, we created a couple users and queried for all of them back.
70 |
71 | 
72 |
73 | Our data shows up within seconds in our Apollo Optics dashboard. Hooray!
74 |
75 | 
76 |
77 | ## **Seeing the Value**
78 |
79 | ### *Trimming out unnecessary calls can save you time and money.*
80 |
81 | Let’s take the Facebook example.
82 |
83 | > If you were Facebook last year, **each second you wasted would have cost you $323** [http://bit.ly/2l6wibD](http://bit.ly/2l6wibD).
84 |
85 | To demonstrate how much money we can save, let’s make a Stripe call to create a new customer in order to simulate Facebook’s payment flow.
86 |
87 | ```graphql
88 | mutation CreateStripeCustomer ($customer: CreateStripeCustomerInput!) {
89 | createStripeCustomer(input: $customer) {
90 | changedStripeCustomer {
91 | id
92 | email
93 | created
94 | }
95 | }
96 | }
97 | ```
98 |
99 | Check it out in your Optics dashboard. Look at how much time that took.
100 |
101 | ### *That request took 219ms*
102 |
103 | 
104 |
105 | Looks like the bottleneck is when we’re asking for the created field to be returned as part of the request. We actually don’t need that value on our client, so let’s remove it from the request.
106 |
107 | ```graphql
108 | mutation CreateStripeCustomer ($customer: CreateStripeCustomerInput!) {
109 | createStripeCustomer(input: $customer) {
110 | changedStripeCustomer {
111 | id
112 | email
113 | }
114 | }
115 | }
116 | ```
117 |
118 | After running this mutation, we can see that the total request time was only 157ms.
119 |
120 | > Request time decreased by over 28%! 🎉
121 |
122 | 
123 |
124 | In dollar amounts, you were able to **save Facebook about $20 each time a new customer was created**. Now, that’s pretty significant.
125 |
126 | Congratulations! You’ve successfully set up powerful analytics to help you optimize your GraphQL API, and saved yourself both time and money.
127 |
128 | Thanks for reading!
129 |
130 | If you’re interested in learning more about analytics, feel free to join our Slack channel and ask us directly, or get started today with Apollo Optics and Scaphold.io!
131 |
132 | Happy to provide more information on how you can optimize your GraphQL API and more.
133 |
--------------------------------------------------------------------------------
/docs/tutorials/ios-push-notifications.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: iOS Push Notifications with GraphQL
3 | date: 2016-02-11 00:00:00 -0700
4 | categories: Scaphold, iOS, Push Notifications, Apple
5 | description: "Using GraphQL to enable Apple push notifications for mobile devices in 4 easy steps."
6 | photo: "https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/banner_ios_push.png"
7 | headshot: "https://assets.scaphold.io/images/vince.jpg"
8 | author: "Vince Ning"
9 | ---
10 |
11 | ## Step-by-step guide on setting up Apple push notifications for your GraphQL backend
12 |
13 | 
14 |
15 | Calling all iOS mobile developers! Today, we’re going to walk you through how to set up Apple push notifications for your GraphQL server.
16 |
17 | > It’s about time for mobile to get some GraphQL love. ❤️
18 |
19 | We’re going to cut right to the chase with the **4 steps** to get you set up right away with iOS push notifications.
20 |
21 | > Prerequisite: You must have an Apple Developer Account and an actual iOS device configured for development. Learn how to configure your device [here](http://apple.stackexchange.com/questions/159196/enable-developer-inside-the-settings-app-on-ios).
22 |
23 | # 1. Create and register your iOS app.
24 |
25 | We’re going to start off by creating an iOS app. To make it easier for you, we’ve provided a starter kit that goes along with this guide.
26 |
27 | [**Download the starter kit here.**](https://github.com/scaphold-io/apple-ios-push-graphql-starter-kit)
28 |
29 | Once we’ve created the app, we’ll want to add it to the Apple Developer Center.
30 |
31 | First, under **Targets > General**, ensure that your Bundle ID is unique.
32 |
33 | 
34 |
35 | Next, under **Targets > Capabilities**, enable Push Notifications.
36 |
37 | 
38 |
39 | Once this happens, you’ll have an App ID associated with your Apple Developer profile that has push capabilities.
40 |
41 | 
42 |
43 | # 2. Obtain certificates (APNS SSL Certificate & App Private Key).
44 |
45 | Here, you’ll be creating certificates to configure your GraphQL server for sending mobile push notification messages.
46 |
47 | ### **APNS SSL Certificate**
48 |
49 | Go to the [Apple Development Center](https://developer.apple.com/account/ios/certificate/) to create a certificate. You can create two types of SSL certificates (Production & Development). We’ll create a Development version for this tutorial.
50 |
51 | 
52 |
53 | Follow along and select the correct App ID associated with your app.
54 |
55 | 
56 |
57 | Using **Keychain Access** on your computer, follow the instructions on the next page to request a certificate from a certificate authority. Define an email address and name for your key. Leave the “CA Email Address” field blank.
58 |
59 | 
60 |
61 | Upload the *CertificateSigningRequest.certSigningRequest* file that was just downloaded on the next screen.
62 |
63 | 
64 |
65 | After successful completion of these steps, make sure you download your generated *.cer* file.
66 |
67 | Now, convert the APNS SSL certificate from *.cer* format to *.pem* format. Using the openssl utility on your computer, run this command in your terminal. Be sure to replace *mynsappcert.cer* with the name of the certificate you downloaded from the Apple Developer site.
68 |
69 | ```
70 | openssl x509 -in myapnsappcert.cer -inform DER -out myapnsappcert.pem
71 | ```
72 |
73 | Hold onto your SSL certificate (*.pem* file) for use later.
74 |
75 | ### **App Private Key**
76 |
77 | In order to create an app private key that’s associated with your SSL certificate, you should export it from the Keychain Access application on your computer. Be sure you’re exporting the *private key*.
78 |
79 | 
80 |
81 | Create a password for your private key and save it as a *.p12* file format.
82 |
83 | 
84 |
85 | After downloading the private key, run this command to convert the app private key from a *.p12* format to a *.pem* format. Again, be sure to replace *mynsappcert.cer* with the name of app private key you just created. You’ll also be prompted to type in the password you created a minute ago for your private key.
86 |
87 | ```
88 | openssl pkcs12 -in myapnsappprivatekey.p12 -out myapnsappprivatekey.pem -nodes -clcerts
89 | ```
90 |
91 | ### **Verify**
92 |
93 | You can verify that your APNS SSL certificate (*.pem*) and your App Private Key (*.pem*) are valid by connecting them to APNS.
94 |
95 | Run this command to do so, and as always, ensure that you’re using your own key names.
96 |
97 | ```
98 | openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert myapnsappcert.pem -key myapnsappprivatekey.pem
99 | ```
100 |
101 | If successful, you should get a response that completes with:
102 |
103 | ```
104 | Verify return code: 0 (ok)
105 | ```
106 |
107 | > *Good work. ✅*
108 |
109 | # 3. Integrate your GraphQL server with APNS.
110 |
111 | Now, it’s time to connect your GraphQL server with APNS so that you can actually send push notifications.
112 |
113 | To make it easy, [Scaphold.io](https://scaphold.io) provides an easy way to set up your very own GraphQL server for free in minutes. [**Sign up for an account**](https://scaphold.io/?signupModal=true) and **create an app**. At this point, you instantly have a GraphQL server deployed in production, ready to make requests.
114 |
115 | Now, go to Scaphold’s Integrations Portal to enable iOS push notifications for development. **Click Add** to begin.
116 |
117 | 
118 |
119 | A form will then pop up, asking for your SSL Certificate file and your App Private Key file. Drop them into the appropriate boxes and **hit Create**.
120 |
121 |
122 |
123 |
124 |
125 | Good job! You’ve now connected your GraphQL server to be able to send push notifications to devices with your app installed.
126 |
127 | # 4. Obtain a device token to send push notifications.
128 |
129 | In order to send a push notification to a device, we need to generate a device token that is a unique identifier for a device and an app together. In that case, we’ll need to install your app onto an actual Apple device.
130 |
131 | But first, we need to create an event handler to receive the device token once a user permits the app to send push notifications to their phone. In your app, you’ll need to put the following snippet in your AppDelegate file:
132 |
133 | ```objectivec
134 | - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
135 | NSLog(@"deviceToken: %@", deviceToken);
136 | }
137 | ```
138 |
139 | We’re going to be using the [boilerplate code from earlier](https://github.com/scaphold-io/apple-ios-push-graphql-starter-kit) to illustrate.
140 |
141 | Now install your app on your Apple device by pressing the big ▶️ button in the top bar of Xcode.
142 |
143 | > *Note: An actual device is needed since the simulator is transient and cannot have a device token associated to it.*
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | Once the app is installed and run, you’ll get a notification that prompts the user to allow push notifications to be sent to the device. And surely, hit Allow.
152 |
153 |
154 | Upon success, your device token will be issued by APNS and printed out in your Xcode console.
155 |
156 |
157 |
158 |
159 | 
160 |
161 | Save this device token and send it to Scaphold to register it on your behalf with APNS. This take the form of sending a GraphQL mutation from your app, but for demonstration purposes, we’ll use GraphiQL to do so.
162 |
163 | 
164 |
165 |
166 |
167 | Congratulations! You’ve successfully configured your app and device to be able to send and receive iOS push notifications. And the best part is that it was all done with GraphQL.
168 |
169 | Now that your GraphQL server is set up for iOS push notifications, the next part of this guide will walk you through how to...
170 |
171 | - Send push notifications
172 | - Associate users to device tokens
173 | - Manage push channels (i.e. groups)
174 |
175 | **Thanks for following along! We'll walk you through sending your first push notification with GraphQL in [the next part of this tutorial](/blog/send-push-notifications-scaphold-graphql/).**
176 |
177 | Looking for how to set up Android push notifications with GCM? [Follow along here!](/blog/android-push-notifications-with-graphql/)
178 |
179 | Enjoy how easy that was to set up a GraphQL server? [**Join Scaphold today!**](https://scaphold.io) And be sure to follow us on [Twitter](https://twitter.com/ScapholdDotIO) or join our [Slack](http://slack.scaphold.io/) for more awesome ways to learn about how to quickly launch your next app with GraphQL!
--------------------------------------------------------------------------------
/docs/tutorials/migrate-mongodb-graphql.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Migrating from MongoDB to GraphQL
3 | date: 2016-01-15 00:01:37 -0700
4 | categories: GraphQL, MongoDB, Migrate, Migration, Parse
5 | description: "Learn how to easily migrate MongoDB to GraphQL!"
6 | photo: "https://assets.scaphold.io/community/blog/migrate-to-mongodb/mongodb.png"
7 | headshot: "https://assets.scaphold.io/images/michael.jpg"
8 | author: "Michael Paris"
9 | ---
10 |
11 | # Migrating from MongoDB to GraphQL
12 |
13 | Hey!
14 |
15 | Today I'm going to demonstrate how I migrated a MongoDB collection to GraphQL. With Parse shutting
16 | down at the end of the month, there are a lot of applications are looking for a new home for their data
17 | and since GraphQL is the future, it's a great time to migrate! I was genuinely surprised by how easy
18 | the process was so here is a little guide so you can migrate your data yourself.
19 |
20 | ## Step 1: Define the schema
21 |
22 | The first step in migrating your data is to prepare your GraphQL schema. I'll be using
23 | MongoDB's sample
24 | restaurants collection found here.
25 | The restaurants collection is defined by a pretty simple Schema. We're going to change a few field names for consistency
26 | and our migration script will handle transforming each data item. Here is our final schema:
27 |
28 | ```graphql
29 | type Restaurant implements Node {
30 | id: ID!
31 | name: String!
32 | mongoId: String!
33 | cuisine: String!
34 | borough: String!
35 | address: Address!
36 | grades: [Grade]
37 | createdAt: DateTime
38 | modifiedAt: DateTime
39 | }
40 |
41 | type Address {
42 | zipcode: String!
43 | street: String!
44 | coord: [Float]
45 | building: String
46 | }
47 |
48 | type Grade {
49 | score: String!
50 | grade: String
51 | date: DateTime!
52 | }
53 | ```
54 |
55 | That's it!
56 |
57 | > Notice that our `Grade` and `Address` types do not implement Node. It is unlikely that `Grades`
58 | or `Addresses` are shared by more than one restaurant so there is no need to create full connections
59 | and thus we don't need to implement `Node`.
60 |
61 | ## Deploy the schema on Scaphold.io
62 |
63 | To get up and running quickly, we'll deploy an API defined by our new schema on Scaphold.io! It takes
64 | about 3 minutes.
65 |
66 | 1. Go to Scaphold.io and create an app.
67 |
68 | 2. You will be taken to the schema designer where you can create the 3 types listed above. Make sure that your
69 | `Address` and `Grade` types do not implement the `Node` interface.
70 |
71 | 3. You're done! Don't worry if you made a mistake, the GraphQL type system will let you know
72 | exactly where you went wrong when you start pushing data.
73 |
74 | > Tip! Watch this video to learn more about the Scaphold Schema Designer
75 |
76 | ## The migration script
77 |
78 | In the industry, a migration task like this is referred to as ETL (Extract, Transform, Load) and
79 | is extremely common. We're going to write a simple node.js script that streams data from our
80 | restaurants.json MongoDB dump into our GraphQL API. Our restaurant data might be too large for the
81 | memory on our machine so we are going to read it in line by line and then queue our API calls so
82 | that we do not run out of network bandwidth on our machine.
83 |
84 | Here is our script! We are using two packages that are not available to a basic node.js installation.
85 | Before running this script make sure you install `async` and `request-promise` via `npm install async request-promise` from your project
86 | directory.
87 |
88 | Take a look at our script.
89 |
90 | ```javascript
91 | // migrator.js
92 | import fs from 'fs';
93 | import readline from 'readline';
94 | import path from 'path';
95 | import request from 'request-promise';
96 | import async from 'async';
97 |
98 | const MONGO_COLLECTION_FILE = path.join(__dirname, 'restaurants.json');
99 |
100 | /*
101 | * Sets our max concurrency to 250 so we don't exhaust
102 | * local network resources
103 | */
104 | const asyncQueue = async.queue((restaurant, callback) => {
105 | createRestaurant(restaurant).then(() => callback());
106 | }, 250);
107 |
108 | /**
109 | * Create a read stream. The mongoexport tool outputs a
110 | * single JSON object per line.
111 | * https://docs.mongodb.com/manual/reference/program/mongoexport/
112 | */
113 | const readStream = fs.createReadStream(MONGO_COLLECTION_FILE);
114 | const lineReader = readline.createInterface({
115 | input: readStream
116 | });
117 | lineReader.on('line', line => {
118 | const restaurant = JSON.parse(line);
119 | /**
120 | * Apply a simple transformation to the restaurant item to adhere
121 | * to our graphql schema.
122 | *
123 | * This takes two steps.
124 | *
125 | * 1) rename restaurant_id to mongoId
126 | * 2) Pull the date out of the nested { $date: ... } structure lying
127 | * under grades.date
128 | *
129 | * Note: The ... below is the JS rest operator which enumerates
130 | * an objects keys and values into another object. You can think
131 | * of it as cloning an object. If you don't use babel then you can
132 | * replace the ... (rest operator) with Object.assign()
133 | *
134 | * It works like this:
135 | * var copy = Object.assign({}, { a: 1 }, { b: 2 });
136 | * console.log(copy); // { a: 1, b: 2 }
137 | */
138 | const modified = {
139 | ...restaurant,
140 | mongoId: restaurant.restaurant_id,
141 | grades: restaurant.grades.map(grade => {
142 | // grade.date currently looks like { $date: }.
143 | // We only want the
144 | return {
145 | ...grade,
146 | date: grade.date['$date']
147 | };
148 | })
149 | };
150 | // Remove the restaurant id so we don't upset the GraphQL type system.
151 | delete modified.restaurant_id;
152 | asyncQueue.push(modified);
153 | });
154 | lineReader.on('error', err => {
155 | console.error(err);
156 | });
157 |
158 | // Make sure you replace the uri with your Scaphold app's url.
159 | const createRestaurant = restaurant => {
160 | const reqOpts = {
161 | method: 'POST',
162 | uri: 'https://us-west-2.api.scaphold.io/graphql/',
163 | body: {
164 | query: `
165 | mutation CreateRestaurant($restaurant: CreateRestaurantInput!) {
166 | createRestaurant(input: $restaurant) {
167 | changedRestaurant {
168 | id
169 | name
170 | }
171 | }
172 | }
173 | `,
174 | variables: {
175 | restaurant
176 | }
177 | },
178 | json: true,
179 | };
180 | return request(reqOpts).then(res => {
181 | console.log(`
182 | Successfully created restaurant: ${res.data.createRestaurant.changedRestaurant.id}
183 | `);
184 | }).catch(err => {
185 | console.log(`Error creating restaurant: ${err.message}`);
186 | });
187 | };
188 | ```
189 |
190 | > The $date syntax from the mongodump exists because mongodb stores its documents using BSON not JSON.
191 | BSON is a binary superset of JSON that adds some additional functionality and thus needs the extra
192 | annotations in order serialize itself to JSON.
193 |
194 | If you're following along all you should have to do now is run your migration script via
195 | `node ./migrator.js`. If everything is setup correctly, your terminal will start printing out
196 | success messages for each item it uploads!
197 |
198 | ## Test the migration
199 |
200 | As soon as it is done, you can immediately start querying your deployed GraphQL API.
201 | Try this one in the GraphiQL tab in the Scaphold portal and/or from your application:
202 |
203 | ```graphql
204 | query AllRestaurants {
205 | viewer {
206 | allRestaurants(first: 50) {
207 | edges {
208 | node {
209 | id
210 | borough
211 | name
212 | mongoId
213 | cuisine
214 | address {
215 | zipcode
216 | building
217 | street
218 | coord
219 | }
220 | grades {
221 | score
222 | grade
223 | date
224 | }
225 | createdAt
226 | }
227 | cursor
228 | }
229 | }
230 | }
231 | }
232 | ```
233 |
234 | ## Migrating more complicated data
235 |
236 | You can use this same process to easily migrate any data to GraphQL. Scaphold offers a couple
237 | features that can make it a lot easier to migrate more complicated data as well. If you have
238 | native relations in your datasets already, take a look at the nested create operators in your
239 | Scaphold API. They allow you create and associate Node implementing types in a single API call.
240 |
241 | For example, assume we had the following types.
242 |
243 | ```graphql
244 | type Post implements Node {
245 | id: ID!
246 | title: String!
247 | category: Category
248 | }
249 |
250 | type Category implements Node {
251 | id: ID!
252 | name: String!
253 | }
254 | ```
255 |
256 | If we had a dataset with a lot of post nodes that were already associated with a category then we
257 | could create our posts as well as associate them with the category with a query like this:
258 |
259 | ```graphql
260 | mutation CreatePostAndAssociateCategory($post:CreatePostInput!) {
261 | createPost(input: $post) {
262 | changedPost {
263 | id
264 | title
265 | category {
266 | id
267 | name
268 | }
269 | }
270 | }
271 | }
272 | ```
273 |
274 | ```javascript
275 | // Variables
276 | {
277 | "post": {
278 | "title": "GraphQL Rocks!",
279 | "category": {
280 | "name": "Next Generation Tech"
281 | }
282 | }
283 | }
284 | ```
285 |
286 | These are just a few techniques we have used to migrate our mongodb data to GraphQL. I hope it helps
287 | and please let me know what you think and if you have any other techniques.
288 |
289 | ### Thanks for reading!
290 |
291 | If you have any questions please let me know below or [Join us on Slack](http://slack.scaphold.io)!
292 |
293 | We'd love to hear what you think and are even more excited to see what you build!
294 |
--------------------------------------------------------------------------------
/docs/tutorials/persisted-queries.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Get Started With Persisted Queries in GraphQL
3 | date: 2016-02-17 00:01:37 -0700
4 | categories: GraphQL, Persisted Queries, Scaphold, Apollo Client
5 | description: "Use persisted queries to optimize your GraphQL powered applications!"
6 | photo: "https://assets.scaphold.io/community/blog/persisted-queries/WithPersistence.png"
7 | headshot: "http://assets.scaphold.io/images/michael.jpg"
8 | author: "Michael Paris"
9 | ---
10 |
11 | # Improve performance with persisted queries
12 |
13 | Today we are happy to announce support for persisted queries on Scaphold.
14 | Persisted queries allow you store the text of your queries on our servers. This allow you to
15 | optimize your app so it doesn't have to send the same query in each request which
16 | can significantly decrease your application's bandwidth usage. Mobile devices with limited connectivity
17 | benefit even more from persisted queries. It's quick and easy to start using persisted queries.
18 | Let's check them out.
19 |
20 | ## How Persisted Queries Work
21 |
22 | A traditional GraphQL request includes a request body that looks like this.
23 |
24 | ```javascript
25 | {
26 | "query": "query GetUser($id: ID!) { getUser(id: $id) { id username } }",
27 | "variables": { "id": 7 }
28 | }
29 | ```
30 |
31 | 
32 |
33 | This query is relatively small, but queries can get much larger. In a large application,
34 | the bandwidth required to send these queries over and over again can have very real impacts on your
35 | application's performance. Persisted queries fix this by transforming the above request into this.
36 |
37 | ```javascript
38 | {
39 | "id": 1,
40 | "variables": { "id": 7 }
41 | }
42 | ```
43 |
44 | 
45 |
46 | Persisted queries require a mapping from query text to an ID. In our example, we would have a
47 | mapping from the **query**:
48 |
49 | ```graphql
50 | query GetUser($id: ID!) { getUser(id: $id) { id username } }
51 | ```
52 |
53 |
54 |
55 | to the **id** `1`.
56 |
57 | These mappings are stored as json documents that have the form
58 |
59 | ```javascript
60 | { [graphql_query]: < ID > }
61 | ```
62 |
63 | For example, the mapping for our query to the right would look like this:
64 |
65 | ```javascript
66 | { "query getUser($id: ID!) { getUser(id: $id) { id username } }": 1 }
67 | ```
68 |
69 | Adding persisted queries to your API is easy! First create the mapping file, then upload it
70 | to Scaphold via the **GraphiQL** tab in the portal. Once you have uploaded the file, you
71 | can begin passing query ids instead of query text to our APIs.
72 |
73 | ## Getting Started with Persisted Queries
74 |
75 | The first step is to create the mapping from our GraphQL queries to their IDs. To do this we
76 | will use the `persistgraphql` npm package. `persistgraphql` expects that you store your
77 | queries as `.graphql` files in your source. If you aren't already doing this, the first step
78 | is to move your queries out to their own `.graphql` files and then `import` those queries from your
79 | components.
80 |
81 | Once you have your queries separated out into their own files then run these commands:
82 |
83 | 1) `npm install -g persistgraphql`
84 |
85 | 2) `persistgraphql < projectdir >`
86 |
87 | 3) This will generate an `extracted_queries.json` file that you will upload to Scaphold.
88 |
89 |
90 | > You can also point `persistgraphql` to a single `.graphql` file. Pointing it to a directory will
91 | recursively pull queries out of every `.graphql` file.
92 |
93 | ## Uploading Your Queries to Scaphold
94 |
95 | Uploading your `extracted_queries.json` file to Scaphold is easy. Go to the GraphiQL tab in your
96 | [Scaphold](https://scaphold.io) portal and click **Persisted Queries** in the page header.
97 |
98 | 
99 |
100 | This will open a side panel where you can drop your `extracted_queries.json` file.
101 |
102 | 
103 |
104 | We will automatically store this mapping so our servers know what query to call for a
105 | particular query ID.
106 |
107 | > We will store your queries in a database and cache them with Redis so you see the full speed benefits.
108 |
109 |
110 |
111 |
112 | ## Add Persisted Queries To Apollo Client
113 |
114 | Apollo Client comes with built-in support for persisted queries. To enable it, replace your
115 | network interface with one that looks like this. This will automatically replace your query text
116 | with the associated query ID without any changes to your code. Scaphold will handle replacing
117 | the ID with the query text on the server.
118 |
119 | ```javascript
120 | import queryMap from ‘./extracted_queries.json’;
121 | import { PersistedQueryNetworkInterface } from ‘persistgraphql’;
122 | const networkInterface = new PersistedQueryNetworkInterface({
123 | queryMap,
124 | uri: apiUrl,
125 | opts: {
126 | headers: {
127 | Authorization: < scaphold auth token >
128 | },
129 | },
130 | });
131 | const client = new ApolloClient({
132 | networkInterface,
133 | });
134 | ```
135 |
136 | ## Using Persisted Queries Without Apollo Client
137 |
138 | Apollo Client makes it easy to use persisted queries but you're welcome to manage the queries
139 | yourself. All you need to do is replace the `"query"` property in each of your requests with
140 | the associated `"id"`. Keep in mind though, that it's up to you to make sure that you have updated
141 | the most recent version of your queries to our servers.
142 |
143 | ```javascript
144 | var request = require('request');
145 |
146 | var data = {
147 | "id": 1,
148 | "variables": {
149 | "id": 7
150 | }
151 | }
152 |
153 | request({
154 | url: "https://us-west-2.api.scaphold.io/graphql/my-awesome-app",
155 | method: "POST",
156 | json: true,
157 | headers: {
158 | "content-type": "application/json",
159 | },
160 | body: data
161 | }, function(error, response, body) {
162 | if (!error && response.statusCode == 200) {
163 | console.log(JSON.stringify(body, null, 2));
164 | } else {
165 | console.log(error);
166 | }
167 | });
168 | ```
169 |
170 | ## Wrapping Up
171 |
172 | I hope you enjoy these new features! Let me know what you think in the comments or connect
173 | with us on slack!
174 |
175 |