├── .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 | Copy and paste the code into your app. -------------------------------------------------------------------------------- /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 | Multiple regions 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 | Add a teammate in side panel 10 | 11 | 2. **Settings Page > Team** 12 | 13 | Add a teammate in Settings 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 | Add Permissions 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 | Relation Permission 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 | add-type 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 | add-field 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 | ![Slackr](https://assets.scaphold.io/tutorials/slackr/slackr.gif) 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 | 5 | 10 | 11 | 12 | 13 | 15 | 16 | 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 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 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 | React Native is the future of mobile development 16 | Angular 17 | Vue2 18 | Vue2 19 | Vue2 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 | ![Slackr](https://assets.scaphold.io/tutorials/slackr/slackr.gif) 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 |
11 |

[**Quickstart Video**](/getting-started/quickstart-video/)

12 |

[**Join our Slack!**](http://slack.scaphold.io)

13 |

[**Need a starter kit?**](/getting-started/starter-kits)

14 |
15 | React Native is the future of mobile development 16 | Angular 17 | Vue2 18 | Vue2 19 | Vue2 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 | ![Digits OAuth Echo Headers](/images/integrations/Digits_Oauth_Echo_Headers.png) 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 | loginUserWithDigits mutation. 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 | ![Configure your Algolia integration](/images/integrations/Algolia_Index.png) 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 | ![Search for a term](/images/integrations/Algolia_Viewer.png) 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 | ![Restaurant Geoloc](/images/integrations/Restaurant_Geo.png) 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 | ![Restaurant Searchable](/images/integrations/Restaurant_Searchable.png) 91 | 92 | 3. Add a new restaurant. 93 | 94 | Chipotle 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 | ![Geo Query](/images/integrations/Geo_Query.png) 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 | ![Set JWT token in header](/images/integrations/Insert_Jwt_Header.png) 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 | ![Link Google Account](/images/integrations/Link_Google_User.png) 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 | Configure your Auth0 apps. 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 | ![Slack](https://forger.typo3.org/images/slack.svg) 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 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 31 | 44 | 45 | 46 | 50 | 55 | 65 | 66 | 67 | 71 | 74 | 87 | 88 | 89 | 93 | 97 | 107 | 108 | 109 | 113 | 117 | 127 | 128 | 129 | 133 | 136 | 146 | 147 | 148 | 152 | 155 | 165 | 166 | 167 |
FrameworkDescriptionStarter Kits
25 | React Native is the future of mobile development 26 |

React

27 |
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 | 32 | 43 |
47 | Angular 48 |

Angular

49 |
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 | 56 | 64 |
68 | Vue - view framework 69 |

Vue 2

70 |
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 | 75 | 86 |
90 | React Native is the future of mobile development 91 |

React Native

92 |
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 | 98 | 106 |
110 | iOS 111 |

iOS

112 |
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 | 118 | 126 |
130 | Android 131 |

Android

132 |
134 | GraphQL tooling on Android is young but is well on its way to being the best way to build Android apps. 135 | 137 | 145 |
149 | Cerebral 150 |

Cerebral

151 |
153 | Cerebral is a next-gen state controller with a built in debugger. It's an awesome tool. 154 | 156 | 164 |
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 | ![](https://assets.scaphold.io/community/blog/social-auth-graphql/social-auth-hero.png) 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 | ![Source: [https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2)](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 | ![](https://assets.scaphold.io/community/blog/social-auth-graphql/auth0-providers.png) 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 | ![](https://assets.scaphold.io/community/blog/social-auth-graphql/auth0-scaphold-app.png) 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 | ![](https://assets.scaphold.io/community/blog/social-auth-graphql/auth0-integration.png) 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 | ![](https://assets.scaphold.io/community/blog/social-auth-graphql/auth-graphiql.png) 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 | ![](https://assets.scaphold.io/community/blog/social-auth-graphql/auth-header.png) 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 |
227 | Scaphold 228 |
Brought to you by Scaphold.io
229 |
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 | ![GraphQL and Android Push Notifications](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/banner_gcm_push.png) 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 | ![Create_Project](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Create_Project.png) 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 | ![Firebase_Console](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Firebase_Console.png) 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 | Create_Firebase_App 41 |
42 | 43 | Follow the steps to finish creating your app, then **Add Firebase to your Android app**. 44 | 45 | ![Add_Firebase](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Add_Firebase.png) 46 | 47 | We’ll need your **Package Name (or Application ID)**. Find your package name here in your app: 48 | 49 | ![Package_Name](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Package_Name.png) 50 | 51 | The **nickname** is your choice. Your first step should look like this. 52 | 53 |
54 | Enter_App_Details 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 | Add_Google_Services_JSON 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 | ![Add_Dependencies_To_Gradle_1](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Add_Dependencies_To_Gradle_1.png) 68 | 69 | - Project-level *bundle.gradle*: 70 | 71 | ![Add_Dependencies_To_Gradle_2](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Add_Dependencies_To_Gradle_2.png) 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 | ![Add_Project_Number](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Add_Project_Number.png) 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 | ![API_Console](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/API_Console.png) 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 | ![Server_API_Key](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Server_API_Key.png) 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 | ![Create_Integration_1](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Create_Integration_1.png) 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 | Create_Integration_2 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 | ![Device_Token](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Device_Token.png) 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 | ![Create_Device_Token](https://assets.scaphold.io/community/blog/android-push-notifications-with-graphql/Create_Device_Token.png) 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 | ![Apollo Optics Dashboard](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Apollo_Optics_Dashboard.png) 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 | ![Create An App](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Create_An_App_2.png) 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 | ![App Settings 1](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Grab_API_Key_1.png) 52 | 53 | *Copy your API key* for use in the next step. 54 | 55 | ![App Settings 2](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Grab_API_Key_2.png) 56 | 57 | 3. **Add Apollo Optics integration on Scaphold.** 58 | 59 | In the Integrations Dashboard, click *Add*. 60 | 61 | ![Scaphold Integrations](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Add_Integration_1.png) 62 | 63 | *Paste your API key* from earlier into the appropriate field, then hit *Create*. 64 | 65 | ![Add Integration](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Add_Integration_2.png) 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 | ![GraphQL Request](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Make_A_Request.png) 72 | 73 | Our data shows up within seconds in our Apollo Optics dashboard. Hooray! 74 | 75 | ![GraphQL Results](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Results.png) 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 | ![Create Customer Call Non-Optimized](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Create_Customer_1.png) 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 | ![Create Customer Call Optimized](https://assets.scaphold.io/community/blog/apollo-optics-and-your-graphql-server/Create_Customer_2.png) 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 | ![GraphQL and Apple Push Notifications](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/banner_ios_push.png) 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 | ![Update Bundle ID](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Update_Bundle_ID.png) 34 | 35 | Next, under **Targets > Capabilities**, enable Push Notifications. 36 | 37 | ![Enable Push Notifications](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Enable_Push_Notifications.png) 38 | 39 | Once this happens, you’ll have an App ID associated with your Apple Developer profile that has push capabilities. 40 | 41 | ![Registered App ID](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Registered_App_ID.png) 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 | ![Provision_SSL_Cert_1](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Provision_SSL_Cert_1.png) 52 | 53 | Follow along and select the correct App ID associated with your app. 54 | 55 | ![Provision_SSL_Cert_2](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Provision_SSL_Cert_2.png) 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 | ![Provision_SSL_Cert_3](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Provision_SSL_Cert_3.png) 60 | 61 | Upload the *CertificateSigningRequest.certSigningRequest* file that was just downloaded on the next screen. 62 | 63 | ![Provision_SSL_Cert_4](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Provision_SSL_Cert_4.png) 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 | ![Provision_Private_Key_1](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Provision_Private_Key_1.png) 80 | 81 | Create a password for your private key and save it as a *.p12* file format. 82 | 83 | ![Provision_Private_Key_2](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Provision_Private_Key_2.png) 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 | ![Create_Integration_1](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Create_Integration_1.png) 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 | Create_Integration_2 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 | Allow_Push 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 | ![Device_Token](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Device_Token.png) 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 | ![Create_Device_Token](https://assets.scaphold.io/community/blog/iOS-push-notifications-with-graphql/Create_Device_Token.png) 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 | ![Persisted Queries](https://assets.scaphold.io/community/blog/persisted-queries/NoPersistence.png) 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 | ![Persisted Queries](https://assets.scaphold.io/community/blog/persisted-queries/WithPersistence.png) 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 | ![Persisted Queries](https://assets.scaphold.io/community/blog/persisted-queries/graphiql-toolbar.png) 99 | 100 | This will open a side panel where you can drop your `extracted_queries.json` file. 101 | 102 | ![Persisted Queries](https://assets.scaphold.io/community/blog/persisted-queries/persist-drop.png) 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 |
176 |

177 | Join us on Slack 178 |

179 |

180 | 181 | 182 | 183 |

184 |
-------------------------------------------------------------------------------- /docs/tutorials/search-with-algolia.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How Algolia Powers Thousands of Apps on Scaphold 3 | date: 2016-02-10 00:00:00 -0700 4 | categories: Scaphold, Algolia, Search 5 | description: "Using GraphQL to provide amazing search experiences." 6 | photo: "https://assets.scaphold.io/community/blog/algolia-powers-thousands-of-scaphold-apps/GraphQL_Algolia2.png" 7 | headshot: "https://assets.scaphold.io/images/vince.jpg" 8 | author: "Vince Ning" 9 | --- 10 | 11 | ## Learn how to integrate Algolia into your GraphQL backend 12 | 13 | ![GraphQL and Algolia](https://assets.scaphold.io/community/blog/algolia-powers-thousands-of-scaphold-apps/GraphQL_Algolia2.png) 14 | 15 | Every day, millions of requests are served by Scaphold.io, a GraphQL backend as a service company that helps people rapidly develop apps. 16 | 17 | > We’ve helped companies like National Geographic, Visa, and software consulting agencies drastically reduce their development cycle from 3 months to 2 weeks. 18 | 19 | At Scaphold, we pride ourselves in not only providing the best app development experience, but also offering the best end-user experiences as possible. We ensure that every request makes it to the client as reliably, efficiently, and as fast as possible with millisecond latencies. 20 | 21 | > Milliseconds really do matter! ⌛️ 22 | 23 | ## **The Problem** 24 | 25 | ### *Apps need any easy way to add search functionality.* 26 | 27 | Scaphold customers come in all shapes and sizes. From indie developers, to small startups and even larger enterprises like National Geographic, Scaphold guarantees their customers the fastest response times across the industry for all types of apps and data. All too often, apps are centralized upon **full-text search** to be able to provide the workflow they want for their users, and we needed a way to address that cleanly and scalably. In addition, many apps include rich location-based features as core components for their product, meaning Scaphold needed to provide geo-indexing for **location-based querying**. For instance, the app might help people find nearby restaurants in a given radius based on their current location. These are very common use cases for modern apps, and it’s no surprise that Scaphold needed to consider various way to support these features. 28 | 29 | ## **The Solution** 30 | 31 | ### *Enter Algolia.* 32 | 33 | We found Algolia since we were both Y Combinator companies. And even after we did our research and scoured the web to find alternative search solutions, the only other one that was in the same league ElasticSearch. 34 | 35 | But then we found this: 36 | 37 | ![Algolia vs. ElasticSearch](https://assets.scaphold.io/community/blog/algolia-powers-thousands-of-scaphold-apps/Algolia_vs_ES.png) 38 | *Source: https://blog.algolia.com/full-text-search-in-your-database-algolia-versus-elasticsearch/* 39 | 40 | Objectively, there are tons of use cases for ElasticSearch, but for our purposes at Scaphold, we needed speed and ease-of-use, and in lieu of our integrations platform, it was a no-brainer to couple Scaphold with Algolia as our search solution. And it covered our customers’ biggest use cases: full-text search and geo-search. 41 | 42 | ## **How It Works** 43 | 44 | ### *Scaphold’s Algolia integration.* 45 | 46 | ![Scaphold Integration Portal](https://assets.scaphold.io/community/blog/algolia-powers-thousands-of-scaphold-apps/Integrations.png) 47 | 48 | Scaphold provides a wide array of integrations to third-party services, allowing developers on our platform to be able to modularly append large chunks of functionality all into their one standardized GraphQL API. In the case of Algolia, all you need is your Application ID and API key. 49 | 50 | > Scaphold will be able to automatically synchronize your data across your app with Algolia’s indexes. 51 | 52 | There are two things going on here under the hood: 53 | 54 | 1. Upon enabling Scaphold’s Algolia integration, you’ll be able to pick which types you want to index, and immediately that data will be searchable using corresponding GraphQL queries that are added to your API. 55 | 56 | 2. Each request that creates, updates, or deletes data of that type will automatically trigger the same operation on that particular Algolia index, ensuring that the data in Algolia always stays in sync with your data on Scaphold. 57 | 58 | ![Sync Data Between Algolia & Scaphold](https://assets.scaphold.io/community/blog/algolia-powers-thousands-of-scaphold-apps/Sync_Data.png) 59 | 60 | 🙌 In a matter of minutes, you’ll have Algolia’s search experiences available through GraphQL on Scaphold’s platform, without having to manage any infrastructure yourself. 61 | 62 |
63 | 64 | ## **Quick Demo** 65 | 66 | ### *Seeing is believing.* 67 | 68 | At Scaphold, we needed a way to demonstrate the power of GraphQL and search working together. So we built a simple Slack messaging app that allows you to send messages in real-time, as well as search through those messages at lightning speeds using GraphQL and Algolia. 69 | 70 | Check it out! 71 | 72 | ![Sync Data Between Algolia & Scaphold](https://assets.scaphold.io/community/blog/algolia-powers-thousands-of-scaphold-apps/Algolia_Demo.gif) 73 | 74 | This was all built in a matter of minutes without having to write any server-side code to set this up. If you want to start playing around with it, we’ve provided the [**full code example on GitHub**](https://github.com/scaphold-io/slackr-graphql-subscriptions-starter-kit/tree/algolia). 75 | 76 | > 🎉 Voilà! 77 | 78 | App developers now have a simple and very well-integrated solution to expose search in their GraphQL APIs with just a few clicks of a button, all thanks to our amazing Algolia integration. Thousands of apps have now been built on Scaphold as a result of this awesome example, and you can [get started today as well](https://scaphold.io)! 79 | 80 | To learn more, follow us on [Twitter](https://twitter.com/ScapholdDotIO) or join our [Slack](http://slack.scaphold.io/)! 81 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Scaphold | GraphQL Tutorials & Documentation 2 | site_description: Documentation for Scaphold.io's GraphQL Backend-as-a-Service. Learn how to rapidly build applications with GraphQL. 3 | site_url: 'https://docs.scaphold.io' 4 | copyright: 'Copyright © 2017 Scaphold, Inc.' 5 | pages: 6 | - Quick Start: index.md 7 | - Starter Kits: starter-kits.md 8 | - Core Data Platform: 9 | - Introduction: coredata/index.md 10 | - The Schema: coredata/schema.md 11 | - Queries: coredata/queries.md 12 | - Mutations: coredata/mutations.md 13 | - Subscriptions: coredata/subscriptions.md 14 | - Connections: coredata/connections-pagination.md 15 | - Authentication: 16 | - Auth Tokens: authentication/tokens.md 17 | - Change Password: authentication/change-password.md 18 | - Permissions: authentication/permissions.md 19 | - Social Auth: authentication/social.md 20 | - Super Users: authentication/super-users.md 21 | - Realtime Subscriptions: 22 | - Subscriptions With Apollo Client: realtime/subscriptions-apollo.md 23 | - Subscriptions With React: realtime/subscriptions-react.md 24 | - Build Slack Tutorial: realtime/slackr-chat-app.md 25 | - Search: 26 | - Full-Text Search: search/fulltext-search.md 27 | - Geo-Search: search/geo-search.md 28 | - Aggregations: 29 | - Basics: aggregations/basics.md 30 | - Advanced: aggregations/advanced.md 31 | - Custom Logic: 32 | - Introduction: custom-logic/index.md 33 | - Function Composition: custom-logic/composition.md 34 | - Pre Operations: custom-logic/pre-operations.md 35 | - Operation Phase: custom-logic/operation-phase.md 36 | - Post Operations: custom-logic/post-operations.md 37 | - Async Operations: custom-logic/async-operations.md 38 | - Logs & Examples: custom-logic/logs.md 39 | - Persisted Queries: 40 | - Introduction: persisted-queries/index.md 41 | - Getting Started: persisted-queries/getting-started.md 42 | - Querying From Client: persisted-queries/querying-from-client.md 43 | - Files: 44 | - Introduction: files/index.md 45 | - Uploading: files/uploading.md 46 | - Querying: files/querying.md 47 | - Integrations: 48 | - Introduction: integrations/index.md 49 | - Email: integrations/email.md 50 | - Monitoring: integrations/monitoring.md 51 | - Passwordless Auth: integrations/passwordless.md 52 | - Push Notifications: integrations/push.md 53 | - Search: integrations/search.md 54 | - Social Auth: 55 | - Setup: integrations/social/setup.md 56 | - Login With Auth0: integrations/social/login-with-auth0.md 57 | - Login With Native SDKs: integrations/social/login-with-native.md 58 | - App Management: 59 | - Aliases: app-management/alias.md 60 | - Analytics: app-management/analytics.md 61 | - Export Schema: app-management/export-schema.md 62 | - Fork App: app-management/fork-app.md 63 | - Teams: app-management/teams.md 64 | - Multi Region: app-management/multi-region.md 65 | - Admin Tokens: app-management/admin-tokens.md 66 | - Token Expiration: app-management/token-expiration.md 67 | - Tutorials: 68 | - Build A Chat App: tutorials/realtime-apps-with-subscriptions.md 69 | - Sending Push Notifications: tutorials/sending-push-notifications.md 70 | - Setup iOS Push: tutorials/ios-push-notifications.md 71 | - Setup Android Push: tutorials/android-push-notifications.md 72 | - Migrating MongoDB: tutorials/migrate-mongodb-graphql.md 73 | - Using Persisted Queries: tutorials/persisted-queries.md 74 | - Advanced Relational Queries: tutorials/advanced-queries.md 75 | - Authentication in GraphQL: tutorials/authentication-in-graphql.md 76 | - Add Search With Algolia: tutorials/search-with-algolia.md 77 | - Aggregations In GraphQL: tutorials/aggregations-in-graphql.md 78 | - Integrate Apollo Optics: tutorials/apollo-optics-graphql-server.md 79 | - Add Social Auth: tutorials/adding-social-auth.md 80 | - Errors: errors.md 81 | - Changelog: changelog.md 82 | - Community: getting-started/community.md 83 | - FAQ: faq.md 84 | theme: 'material' 85 | extra_css: 86 | - css/base.css 87 | - css/styles.css 88 | repo_url: https://github.com/scaphold-io/scaphold-docs 89 | site_favicon: images/favicon.ico 90 | markdown_extensions: 91 | - admonition 92 | - codehilite: 93 | linenums: true 94 | use_pygments: true 95 | - toc(permalink=true) 96 | - pymdownx.emoji: 97 | emoji_generator: !!python/name:pymdownx.emoji.to_svg 98 | - pymdownx.inlinehilite 99 | - pymdownx.superfences 100 | - pymdownx.tasklist(custom_checkbox=true) 101 | extra: 102 | logo: 'images/home.png' 103 | font: 104 | text: 'Avenir' 105 | code: 'Mono' 106 | social: 107 | - type: 'github' 108 | link: 'https://github.com/scaphold-io' 109 | - type: 'twitter' 110 | link: 'https://twitter.com/ScapholdDotIO' 111 | - type: 'facebook' 112 | link: 'https://www.facebook.com/scaphold/' 113 | - type: 'youtube' 114 | link: 'https://www.youtube.com/channel/UC3CKbmn1jYp5fRoCIiBrsGg' 115 | - type: 'slack' 116 | link: 'http://slack.scaphold.io' 117 | - type: 'linkedin' 118 | link: 'https://www.linkedin.com/company/10780129' 119 | - type: 'envelope' 120 | link: 'mailto:hi@scaphold.io' 121 | google_analytics: ['UA-93897607-1', 'auto'] -------------------------------------------------------------------------------- /theme/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block site_nav %} 4 | 5 | {% if nav %} 6 |
8 |
9 |
10 | {% include "partials/nav.html" %} 11 |
12 |
13 |
14 | {% endif %} 15 | {% endblock %} -------------------------------------------------------------------------------- /theme/partials/toc.html: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------