diff --git a/docs/Authentication/config.mdx b/docs/Authentication/config.mdx index 7fb944d302..b0842e2f56 100644 --- a/docs/Authentication/config.mdx +++ b/docs/Authentication/config.mdx @@ -34,6 +34,10 @@ Technically, both of these options will work for third-party integrations but th To enable API keys on a collection, set the `useAPIKey` auth option to `true`. From there, a new interface will appear in the Admin panel for each document within the collection that allows you to generate an API key for each user in the Collection. + + User API keys are encrypted within the database, meaning that if your database is compromised, your API keys will not be. + + ##### Authenticating via API Key To utilize your API key while interacting with the REST or GraphQL API, add the `Authorization` header. diff --git a/docs/Authentication/overview.mdx b/docs/Authentication/overview.mdx index b2f41fc807..604ddd9763 100644 --- a/docs/Authentication/overview.mdx +++ b/docs/Authentication/overview.mdx @@ -107,7 +107,7 @@ For more about how to automatically include cookies in requests from your app to ### CSRF Protection -CSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes any XSS vulnerabilities, however, CSRF attacks can still be possible. +CSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes many XSS vulnerabilities, however, CSRF attacks can still be possible. For example, let's say you have a very popular app running at coolsite.com. This app allows users to manage finances and send / receive money. As Payload is using HTTP-only cookies, that means that browsers automatically will include cookies when sending requests to your domain - no matter what page created the request. diff --git a/docs/Configuration/overview.mdx b/docs/Configuration/overview.mdx index e05a3eaa2e..b82675d4e5 100644 --- a/docs/Configuration/overview.mdx +++ b/docs/Configuration/overview.mdx @@ -20,7 +20,7 @@ Payload is a *config-based*, code-first CMS and application framework. The Paylo | `globals` | An array of all Globals that Payload will manage. For more on Globals and their configs, [click here](/docs/configuration/globals). | | `admin` | Base Payload admin configuration. Specify custom components, control metadata, set the Admin user collection, and [more](/docs/admin/overview#options). | | `localization` | Opt-in and control how Payload handles the translation of your content into multiple locales. [More](/docs/configuration/localization) | -| `graphQL` | Manage GraphQL-specific functionality here. Define your own queries and mutations, manage query complexity limits, and [more](/docs/graphql/config). | +| `graphQL` | Manage GraphQL-specific functionality here. Define your own queries and mutations, manage query complexity limits, and [more](/docs/graphql/overview#config). | | `cookiePrefix` | A string that will be prefixed to all cookies that Payload sets. | | `cors` | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. | | `csrf` | A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. [More](/docs/authentication/overview#csrf-protection) | diff --git a/docs/GraphQL/config.mdx b/docs/GraphQL/config.mdx deleted file mode 100644 index c904c8f454..0000000000 --- a/docs/GraphQL/config.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: GraphQL Overview -label: Overview -order: 10 ---- - -Go over GraphQL configuration options. - -- Naming conventions -- List of all queries and mutations w/ examples -- Context diff --git a/docs/GraphQL/extending.mdx b/docs/GraphQL/extending.mdx index 3932cf6641..9332b684d8 100644 --- a/docs/GraphQL/extending.mdx +++ b/docs/GraphQL/extending.mdx @@ -4,4 +4,65 @@ label: Custom Queries and Mutations order: 20 --- -Talk about how to add your own queries and mutations. +You can add your own GraphQL queries and mutations to Payload, making use of all the types that Payload has defined for you. + +To do so, add your queries and mutations to the main Payload config as follows: + +| Config Path | Description | +| -------------------- | -------------| +| `graphQL.queries` | Function that returns an object containing keys to custom GraphQL queries | +| `graphQL.mutations` | Function that returns an object containing keys to custom GraphQL mutations | + +The above properties each receive a function that is defined with the following arguments: + +**`GraphQL`** + +This is Payload's GraphQL dependency. You should not install your own copy of GraphQL as a dependency due to underlying restrictions based on how GraphQL works. Instead, you can use the Payload-provided copy via this argument. + +**`payload`** + +This is a copy of the currently running Payload instance, which provides you with existing GraphQL types for all of your Collections and Globals - among other things. + +##### Return value + +Both `graphQL.queries` and `graphQL.mutations` functions should return an object with properties equal to your newly written GraphQL queries and mutations. + +### Example + +`payload.config.js`: + +```js +import { buildConfig } from 'payload/config'; +import myCustomQueryResolver from './graphQL/resolvers/myCustomQueryResolver'; + +export default buildConfig({ + serverURL: 'http://localhost:3000', + graphQL: { + // highlight-start + queries: (GraphQL, payload) => { + return { + MyCustomQuery: { + type: new GraphQL.GraphQLObjectType({ + name: 'MyCustomQuery', + fields: { + text: { + type: GraphQL.GraphQLString, + }, + someNumberField: { + type: GraphQL.GraphQLFloat, + }, + }, + args: { + argNameHere: { + type: new GraphQL.GraphQLNonNull(GraphQLString), + } + }, + resolve: myCustomQueryResolver, + }) + } + } + } + // highlight-end + } +}) +``` diff --git a/docs/GraphQL/overview.mdx b/docs/GraphQL/overview.mdx new file mode 100644 index 0000000000..5be288520b --- /dev/null +++ b/docs/GraphQL/overview.mdx @@ -0,0 +1,93 @@ +--- +title: GraphQL Overview +label: Overview +order: 10 +--- + +In addition to its REST and Local APIs, Payload ships with a fully featured and extensible GraphQL API. + +By default, the GraphQL API is exposed via `/api/graphql`, but you can customize this URL via specifying your `routes` within the main Payload config. + +The labels you provide for your Collections and Globals are used to format the GraphQL types that are created to correspond to your config. Special characters and spaces are removed. + +### Collections + +Everything that can be done to a Collection via the REST or Local API can be done with GraphQL (outside of uploading files, which is REST-only). If you have a collection as follows: + +```js +const Post = { + slug: 'public-users', + auth: true, // Auth is enabled + labels: { + singular: 'Public User', + plural: 'Public Users', + }, + fields: [ + ... + ], +} +``` + +**Payload will automatically open up the following queries:** + +| Query Name | Operation | +| ---------------------- | -------------| +| **`PublicUser`** | `findByID` | +| **`PublicUsers`** | `find` | +| **`mePublicUser`** | `me` auth operation | + +**And the following mutations:** + +| Query Name | Operation | +| ------------------------------ | -------------| +| **`createPublicUser`** | `create` | +| **`updatePublicUser`** | `update` | +| **`deletePublicUser`** | `delete` | +| **`forgotPasswordPublicUser`** | `forgotPassword` auth operation | +| **`resetPasswordPublicUser`** | `resetPassword` auth operation | +| **`unlockPublicUser`** | `unlock` auth operation | +| **`verifyPublicUser`** | `verify` auth operation | +| **`loginPublicUser`** | `login` auth operation | +| **`logoutPublicUser`** | `logout` auth operation | +| **`refreshTokenPublicUser`** | `refresh` auth operation | + +### Globals + +Globals are also fully supported. For example: + +```js +const Header = { + slug: 'header', + label: 'Header', + fields: [ + ... + ], +} +``` + +**Payload will open the following query:** + +| Query Name | Operation | +| ---------------------- | -------------| +| **`Header`** | `findOne` | + +**And the following mutation:** + +| Query Name | Operation | +| ---------------------- | -------------| +| **`updateHeader`** | `update` | + +### GraphQL Playground + +GraphQL Playground is enabled by default for development purposes, but disabled in production. You can enable it in production by passing `graphQL.disablePlaygroundInProduction` a `false` setting in the main Payload config. + +You can even log in using the `login` mutation to use the Playground as an authenticated user. + + + Tip:
+ To see more regarding how the above queries and mutations are used, visit your GraphQL playground (by default at (http://localhost:3000/api/graphql-playground) while your server is running. There, you can use the "Schema" and "Docs" buttons on the right to see a ton of detail about how GraphQL operates within Payload. +
+ +### Query complexity limits + +Payload comes with a built-in query complexity limiter to prevent bad people from trying to slow down your server by running massive queries. To learn more, [click here](/docs/production/security#graphql-complexity). diff --git a/docs/GraphQL/playground.mdx b/docs/GraphQL/playground.mdx deleted file mode 100644 index b22d03bde4..0000000000 --- a/docs/GraphQL/playground.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Using the GraphQL Playground -label: Playground -order: 30 ---- - -Talk about the GraphQL Playground - -Talk about how to enable / disable it in production - -Talk about how to log in diff --git a/docs/Guides/blog-api.mdx b/docs/Guides/blog-api.mdx deleted file mode 100644 index 1155e7e024..0000000000 --- a/docs/Guides/blog-api.mdx +++ /dev/null @@ -1,253 +0,0 @@ ---- -title: Blog API From Scratch -label: Blog API -order: 20 ---- - -In this guide, we will be creating a Blog API from scratch. - -Prerequisites: - -- Node -- npm or yarn -- MongoDB server - -## Initial Setup - -Let's get started by creating a project in a blank directory - -```sh -yarn init -y -``` - -We then will install Payload and Express - -```sh -yarn add payload express -``` - -### Server - -Create a file named `server.js` in the root folder with the following contents - -```js -const express = require('express'); -const app = express(); - -app.listen(3000, async () => { - console.log('Express is now listening for incoming connections on port 3000.'); -}); -``` - -This is a basic Express application. Now let's pull in Payload and fill in the `init` parameters. `secret` and `mongoURL` can be changed as needed. - -```js -const express = require('express'); -const payload = require('payload'); // highlight-line - -const app = express(); - -// highlight-start -payload.init({ - secret: 'SECRET_KEY', - mongoURL: 'mongodb://localhost/payload-blog', - express: app, -}) -// highlight-end - -app.listen(3000, async () => { - console.log('Express is now listening for incoming connections on port 3000.'); -}); -``` - -### Payload Configuration - -Next, let's get some basics put into our `payload.config.js` file - -```js -module.exports = { - serverURL: 'http://localhost:3000', - collections: [], -} -``` - -#### Collections - -We'll need a few different collections for our blog: - -- Posts -- Categories -- Tags - -The Posts will have the ability to have a single category and multiple tags - -Let's create a `collections` directory and create a `.js` file for each - -```js:title=Posts.js -module.exports = { - slug: 'posts', - labels: { - singular: 'Post', - plural: 'Posts', - }, - admin: { - useAsTitle: 'title', - }, - fields: [ - { - name: 'title', - label: 'Title', - type: 'text' - }, - { - name: 'content', - label: 'Content', - type: 'textarea', - }, - { - name: 'category', - label: 'Category', - type: 'relationship', - relationTo: 'categories', // Categories Slug // highlight-line - }, - { - name: 'tags', - label: 'Tags', - type: 'relationship', - relationTo: 'tags', // Tags Slug // highlight-line - hasMany: true, // Allow multiple tags per post - }, - ] -}; -``` - -```js:title=Categories.js -module.exports = { - slug: 'categories', - labels: { - singular: 'Category', - plural: 'Categories', - }, - admin: { - useAsTitle: 'name', - }, - fields: [ - { - name: 'name', - label: 'Name', - type: 'text', - }, - ], -}; -``` - -```js:title=Tags.js -module.exports = { - slug: 'tags', - labels: { - singular: 'Tag', - plural: 'Tags', - }, - admin: { - useAsTitle: 'name', - }, - fields: [ - { - name: 'name', - label: 'Name', - type: 'text', - }, - ], -}; -``` - -Once these are created, we can pull them into the `collections` array in our `payload.config.js` - -```js:title=payload.config.js -const Admins = require('./collections/Admins'); -// highlight-start -const Posts = require('./collections/Posts'); -const Categories = require('./collections/Categories'); -const Tags = require('./collections/Tags'); -// highlight-end - -module.exports = { - serverURL: 'http://localhost:3000', - admin: { - user: 'admins', - }, - collections: [ - Admins, - // highlight-start - Posts, - Categories, - Tags - // highlight-end - ], -} -``` - -### Create Content - -**TODO: Should a guide like this use the Admin interface or `curl`?** - -Navigate to the admin interface at `http://localhost:3000/admin` and create your first user. - -Then create some Categories, Tags, and a Post. - -Let's use `curl` to quickly create some data. The following commands will create 2 tags and 1 category. - -```sh -curl -H "Content-Type: application/json" -X POST -d '{"name": "JavaScript"}' http://localhost:3000/api/tags -curl -H "Content-Type: application/json" -X POST -d '{"name": "TypeScript"}' http://localhost:3000/api/tags -curl -H "Content-Type: application/json" -X POST -d '{"name": "Code"}' http://localhost:3000/api/categories -``` - -We'll then make a request to create a Post, this will inclue a relationship to the category and tags created previously. - -TODO: Somehow retrieve tag and category IDs then include them in a request to create a Post - -Once complete, navigate to `http://localhost:3000/api/posts`, and you should see something similar to the following: - -```js -{ - "docs": [ - { - "tags": [ - { - "name": "TypeScript", - "createdAt": "2020-11-13T11:48:05.993Z", - "updatedAt": "2020-11-13T11:48:05.993Z", - "id": "5fae72758315da656fb3a8f0" - }, - { - "name": "JavaScript", - "createdAt": "2020-11-13T11:48:00.064Z", - "updatedAt": "2020-11-13T11:48:00.064Z", - "id": "5fae72708315da656fb3a8ef" - } - ], - "title": "My Title", - "content": "This is some content", - "category": { - "name": "Code", - "createdAt": "2020-11-13T11:48:10.351Z", - "updatedAt": "2020-11-13T11:48:36.358Z", - "id": "5fae727a8315da656fb3a8f1" - }, - "createdAt": "2020-11-13T11:50:14.312Z", - "updatedAt": "2020-11-13T11:50:14.312Z", - "id": "5fae72f68e314b67609e05d1" - } - ], - "totalDocs": 1, - "limit": 10, - "totalPages": 1, - "page": 1, - "pagingCounter": 1, - "hasPrevPage": false, - "hasNextPage": false, - "prevPage": null, - "nextPage": null -} -``` diff --git a/docs/Guides/configure-email.mdx b/docs/Guides/configure-email.mdx deleted file mode 100644 index 188ccb0cec..0000000000 --- a/docs/Guides/configure-email.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Configure Email -label: Configure Email -order: 30 ---- - -TODO: Configure SendGrid or similar diff --git a/docs/Production/security.mdx b/docs/Production/security.mdx index c68063bb10..ecc338926c 100644 --- a/docs/Production/security.mdx +++ b/docs/Production/security.mdx @@ -10,3 +10,7 @@ order: 20 - Max Depth - CSRF - CORS + +### Limiting GraphQL Complexity + +^^ Keep this header here