mirror of
https://github.com/strapi/strapi.git
synced 2025-09-03 22:03:08 +00:00
Move graphql doc in plugins section and update examples to match FoodAdvisor
This commit is contained in:
parent
9bfcad2a54
commit
f02d4dfcb0
@ -110,7 +110,6 @@ module.exports = {
|
|||||||
'/3.0.0-beta.x/guides/email',
|
'/3.0.0-beta.x/guides/email',
|
||||||
'/3.0.0-beta.x/guides/upload',
|
'/3.0.0-beta.x/guides/upload',
|
||||||
'/3.0.0-beta.x/guides/parameters',
|
'/3.0.0-beta.x/guides/parameters',
|
||||||
'/3.0.0-beta.x/guides/graphql',
|
|
||||||
'/3.0.0-beta.x/guides/i18n',
|
'/3.0.0-beta.x/guides/i18n',
|
||||||
'/3.0.0-beta.x/guides/models',
|
'/3.0.0-beta.x/guides/models',
|
||||||
'/3.0.0-beta.x/guides/policies',
|
'/3.0.0-beta.x/guides/policies',
|
||||||
@ -141,6 +140,7 @@ module.exports = {
|
|||||||
'/3.0.0-beta.x/plugins/users-permissions',
|
'/3.0.0-beta.x/plugins/users-permissions',
|
||||||
'/3.0.0-beta.x/plugins/documentation',
|
'/3.0.0-beta.x/plugins/documentation',
|
||||||
'/3.0.0-beta.x/plugins/upload',
|
'/3.0.0-beta.x/plugins/upload',
|
||||||
|
'/3.0.0-beta.x/plugins/graphql',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
# GraphQL
|
# GraphQL
|
||||||
|
|
||||||
::: note
|
By default Strapi create [REST endpoints](../content-api/api-endpoints) for each of your content types. With the GraphQL plugin, you will be able to add a GraphQL endpoint to fetch and mutate your content.
|
||||||
This feature requires the GraphQL plugin (not installed by default).
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -36,7 +34,7 @@ strapi install graphql
|
|||||||
|
|
||||||
::::
|
::::
|
||||||
|
|
||||||
Then, start your app and open your browser at [http://localhost:1337/graphql](http://localhost:1337/graphql). You should see the interface (GraphQL Playground) that will help you to write GraphQL query to explore your data.
|
Then, start your app and open your browser at [http://localhost:1337/graphql](http://localhost:1337/graphql). You should see the interface (**GraphQL Playground**) that will help you to write GraphQL query to explore your data.
|
||||||
|
|
||||||
::: note
|
::: note
|
||||||
Install the [ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj/related) extension to set the `Authorization` header in your request
|
Install the [ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj/related) extension to set the `Authorization` header in your request
|
||||||
@ -54,7 +52,7 @@ You can edit these configurations by creating following file.
|
|||||||
|
|
||||||
**Path —** `./extensions/graphql/config/settings.json`.
|
**Path —** `./extensions/graphql/config/settings.json`.
|
||||||
|
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
"endpoint": "/graphql",
|
"endpoint": "/graphql",
|
||||||
"tracing": false,
|
"tracing": false,
|
||||||
@ -65,11 +63,11 @@ You can edit these configurations by creating following file.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Query API
|
## Query API
|
||||||
|
|
||||||
In the section, we assume that the [Shadow CRUD](#shadow-crud) feature is enabled. For each model, the plugin auto-generates queries and mutations which just fit to your needs.
|
In the section, we assume that the [Shadow CRUD](#shadow-crud) feature is enabled. For each model, the plugin auto-generates queries and mutations which just fit to your needs.
|
||||||
|
|
||||||
#### Fetch a single entry
|
### Fetch a single entry
|
||||||
|
|
||||||
- `id`: String
|
- `id`: String
|
||||||
|
|
||||||
@ -82,7 +80,7 @@ query {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Fetch multiple entries
|
### Fetch multiple entries
|
||||||
|
|
||||||
```
|
```
|
||||||
query {
|
query {
|
||||||
@ -93,7 +91,7 @@ query {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Create a new entry
|
### Create a new entry
|
||||||
|
|
||||||
- `input`: Object
|
- `input`: Object
|
||||||
- `data`: Object — Values to insert
|
- `data`: Object — Values to insert
|
||||||
@ -114,7 +112,7 @@ mutation {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The implementation of the mutations also supports relational attributes. For example, you can create a new `User` and attach many `Post` to it by writing your query like this:
|
The implementation of the mutations also supports relational attributes. For example, you can create a new `User` and attach many `Restaurant` to it by writing your query like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
mutation {
|
mutation {
|
||||||
@ -122,23 +120,23 @@ mutation {
|
|||||||
data: {
|
data: {
|
||||||
username: "John",
|
username: "John",
|
||||||
email: "john@doe.com",
|
email: "john@doe.com",
|
||||||
posts: ["5b51e3949db573a586ad22de", "5b5b26619b0820c1c2fb79c9"]
|
restaurants: ["5b51e3949db573a586ad22de", "5b5b26619b0820c1c2fb79c9"]
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
user {
|
user {
|
||||||
username
|
username
|
||||||
email
|
email
|
||||||
posts {
|
restaurant {
|
||||||
title
|
name
|
||||||
content
|
description
|
||||||
publishedAt
|
price
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Update an existing entry
|
### Update an existing entry
|
||||||
|
|
||||||
- `input`: Object
|
- `input`: Object
|
||||||
- `where`: Object - Entry's ID to update
|
- `where`: Object - Entry's ID to update
|
||||||
@ -167,16 +165,16 @@ You can also update relational attributes by passing an ID or an array of IDs (d
|
|||||||
|
|
||||||
```
|
```
|
||||||
mutation {
|
mutation {
|
||||||
updatePost(input: {
|
updateRestaurant(input: {
|
||||||
where: {
|
where: {
|
||||||
id: "5b5b27f8164f75c29c728110"
|
id: "5b5b27f8164f75c29c728110"
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
author: "5b51e3949db573a586ad22de" // User ID
|
chef: "5b51e3949db573a586ad22de" // User ID
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
post {
|
restaurant {
|
||||||
author {
|
chef {
|
||||||
username
|
username
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
@ -185,7 +183,7 @@ mutation {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Delete an entry
|
### Delete an entry
|
||||||
|
|
||||||
- `input`: Object
|
- `input`: Object
|
||||||
- `where`: Object - Entry's ID to delete
|
- `where`: Object - Entry's ID to delete
|
||||||
@ -235,9 +233,9 @@ query {
|
|||||||
username
|
username
|
||||||
email
|
email
|
||||||
},
|
},
|
||||||
books(limit: 10, where: { _id_nin: ["5c4dad1a8f3845222ca88a56", "5c4dad1a8f3845222ca88a57"] }) {
|
restaurants(limit: 10, where: { _id_nin: ["5c4dad1a8f3845222ca88a56", "5c4dad1a8f3845222ca88a57"] }) {
|
||||||
_id,
|
_id,
|
||||||
title
|
name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -261,24 +259,24 @@ To simplify and automate the build of the GraphQL schema, we introduced the Shad
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
If you've generated an API called `Post` using the CLI `strapi generate:api post` or the administration panel, your model looks like this:
|
If you've generated an API called `Restaurant` using the CLI `strapi generate:api restaurant` or the administration panel, your model looks like this:
|
||||||
|
|
||||||
**Path —** `./api/post/models/Post.settings.json`.
|
**Path —** `./api/restaurant/models/Restaurant.settings.json`.
|
||||||
|
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
"connection": "default",
|
"connection": "default",
|
||||||
"options": {
|
"options": {
|
||||||
"timestamps": true
|
"timestamps": true
|
||||||
},
|
},
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"title": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
},
|
||||||
"content": {
|
"description": {
|
||||||
"type": "text"
|
"type": "text"
|
||||||
},
|
},
|
||||||
"published": {
|
"open": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,31 +286,31 @@ If you've generated an API called `Post` using the CLI `strapi generate:api post
|
|||||||
The generated GraphQL type and queries will be:
|
The generated GraphQL type and queries will be:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Post's Type definition
|
// Restaurant's Type definition
|
||||||
type Post {
|
type Restaurant {
|
||||||
_id: String
|
_id: String
|
||||||
created_at: String
|
created_at: String
|
||||||
updated_at: String
|
updated_at: String
|
||||||
title: String
|
name: String
|
||||||
content: String
|
description: String
|
||||||
published: Boolean
|
open: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queries to retrieve one or multiple posts.
|
// Queries to retrieve one or multiple restaurants.
|
||||||
type Query {
|
type Query {
|
||||||
posts(sort: String, limit: Int, start: Int, where: JSON): [Post]
|
restaurants(sort: String, limit: Int, start: Int, where: JSON): [Restaurant]
|
||||||
post(id: String!): Post
|
restaurant(id: String!): Restaurant
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutations to create, update or delete a post.
|
// Mutations to create, update or delete a restaurant.
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createPost(input: createPostInput): createPostPayload!
|
createRestaurant(input: createRestaurantInput): createRestaurantPayload!
|
||||||
updatePost(input: updatePostInput): updatePostPayload!
|
updateRestaurant(input: updateRestaurantInput): updateRestaurantPayload!
|
||||||
deletePost(input: deletePostInput): deletePostPayload!
|
deleteRestaurant(input: deleteRestaurantInput): deleteRestaurantPayload!
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The queries and mutations will use the generated controller's actions as resolvers. It means that the `posts` query will execute the `Post.find` action, the `post` query will use the `Post.findOne` action and the `createProduct` mutation will use the `Post.create` action, etc.
|
The queries and mutations will use the generated controller's actions as resolvers. It means that the `restaurants` query will execute the `Restaurant.find` action, the `restaurant` query will use the `Restaurant.findOne` action and the `createRestaurant` mutation will use the `Restaurant.create` action, etc.
|
||||||
|
|
||||||
## Aggregation & Grouping
|
## Aggregation & Grouping
|
||||||
|
|
||||||
@ -324,14 +322,14 @@ Strapi now supports Aggregation & Grouping.
|
|||||||
Let's consider again the model mentioned above:
|
Let's consider again the model mentioned above:
|
||||||
|
|
||||||
```
|
```
|
||||||
type Post {
|
type Restaurant {
|
||||||
_id: ID
|
_id: ID
|
||||||
createdAt: String
|
createdAt: String
|
||||||
updatedAt: String
|
updatedAt: String
|
||||||
title: String
|
name: String
|
||||||
content: String
|
description: String
|
||||||
nb_likes: Int,
|
nb_likes: Int,
|
||||||
published: Boolean
|
open: Boolean
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -340,52 +338,52 @@ Strapi will generate automatically for you the following queries & types:
|
|||||||
### Aggregation
|
### Aggregation
|
||||||
|
|
||||||
```
|
```
|
||||||
type PostConnection {
|
type RestaurantConnection {
|
||||||
values: [Post]
|
values: [Restaurant]
|
||||||
groupBy: PostGroupBy
|
groupBy: RestaurantGroupBy
|
||||||
aggregate: PostAggregator
|
aggregate: RestaurantAggregator
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostGroupBy {
|
type RestaurantGroupBy {
|
||||||
_id: [PostConnection_id]
|
_id: [RestaurantConnection_id]
|
||||||
createdAt: [PostConnectionCreatedAt]
|
createdAt: [RestaurantConnectionCreatedAt]
|
||||||
updatedAt: [PostConnectionUpdatedAt]
|
updatedAt: [RestaurantConnectionUpdatedAt]
|
||||||
title: [PostConnectionTitle]
|
name: [RestaurantConnectionTitle]
|
||||||
content: [PostConnectionContent]
|
description: [RestaurantConnectionContent]
|
||||||
nb_likes: [PostConnectionNbLikes],
|
nb_likes: [RestaurantConnectionNbLikes],
|
||||||
published: [PostConnectionPublished]
|
open: [RestaurantConnectionPublished]
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostConnectionPublished {
|
type RestaurantConnectionPublished {
|
||||||
key: Boolean
|
key: Boolean
|
||||||
connection: PostConnection
|
connection: RestaurantConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostAggregator {
|
type RestaurantAggregator {
|
||||||
count: Int
|
count: Int
|
||||||
sum: PostAggregatorSum
|
sum: RestaurantAggregatorSum
|
||||||
avg: PostAggregatorAvg
|
avg: RestaurantAggregatorAvg
|
||||||
min: PostAggregatorMin
|
min: RestaurantAggregatorMin
|
||||||
max: PostAggregatorMax
|
max: RestaurantAggregatorMax
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostAggregatorAvg {
|
type RestaurantAggregatorAvg {
|
||||||
nb_likes: Float
|
nb_likes: Float
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostAggregatorMin { // Same for max and sum
|
type RestaurantAggregatorMin { // Same for max and sum
|
||||||
nb_likes: Int
|
nb_likes: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
postsConnection(sort: String, limit: Int, start: Int, where: JSON): PostConnection
|
restaurantsConnection(sort: String, limit: Int, start: Int, where: JSON): RestaurantConnection
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Getting the total count and the average likes of posts:
|
Getting the total count and the average likes of restaurants:
|
||||||
|
|
||||||
```
|
```
|
||||||
postsConnection {
|
restaurantsConnection {
|
||||||
aggregate {
|
aggregate {
|
||||||
count
|
count
|
||||||
avg {
|
avg {
|
||||||
@ -396,10 +394,10 @@ postsConnection {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's say we want to do the same query but for only published posts
|
Let's say we want to do the same query but for only open restaurants
|
||||||
|
|
||||||
```
|
```
|
||||||
postsConnection(where: { published: true }) {
|
restaurantsConnection(where: { open: true }) {
|
||||||
aggregate {
|
aggregate {
|
||||||
count
|
count
|
||||||
avg {
|
avg {
|
||||||
@ -410,12 +408,12 @@ postsConnection(where: { published: true }) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Getting the average likes of published and unpublished posts
|
Getting the average likes of open and non open restaurants
|
||||||
|
|
||||||
```
|
```
|
||||||
postsConnection {
|
restaurantsConnection {
|
||||||
groupBy {
|
groupBy {
|
||||||
published: {
|
open: {
|
||||||
key
|
key
|
||||||
connection {
|
connection {
|
||||||
aggregate {
|
aggregate {
|
||||||
@ -431,12 +429,12 @@ postsConnection {
|
|||||||
|
|
||||||
Result
|
Result
|
||||||
|
|
||||||
```JSON
|
```json
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
postsConnection: {
|
restaurantsConnection: {
|
||||||
groupBy: {
|
groupBy: {
|
||||||
published: [
|
open: [
|
||||||
{
|
{
|
||||||
key: true,
|
key: true,
|
||||||
connection: {
|
connection: {
|
||||||
@ -491,57 +489,55 @@ module.exports = {
|
|||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
Let say we are using the same previous `Post` model.
|
Let say we are using the same previous `Restaurant` model.
|
||||||
|
|
||||||
**Path —** `./api/post/config/schema.graphql`.
|
**Path —** `./api/restaurant/config/schema.graphql`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
module.exports = {
|
module.exports = {
|
||||||
definition: `
|
definition: `
|
||||||
enum PostStatusInput {
|
enum RestaurantStatusInput {
|
||||||
draft
|
work
|
||||||
reviewing
|
open
|
||||||
reviewed
|
closed
|
||||||
published
|
|
||||||
deleted
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
query: `
|
query: `
|
||||||
postsByAuthor(id: ID, status: PostStatusInput, limit: Int): [Post]!
|
restaurantsByChef(id: ID, status: RestaurantStatusInput, limit: Int): [Restaurant]!
|
||||||
`,
|
`,
|
||||||
mutation: `
|
mutation: `
|
||||||
attachPostToAuthor(id: ID, authorID: ID): Post!
|
attachRestaurantToChef(id: ID, chefID: ID): Restaurant!
|
||||||
`
|
`
|
||||||
resolver: {
|
resolver: {
|
||||||
Query: {
|
Query: {
|
||||||
post: {
|
restaurant: {
|
||||||
description: 'Return a single post',
|
description: 'Return a single restaurant',
|
||||||
policies: ['plugins.users-permissions.isAuthenticated', 'isOwner'], // Apply the 'isAuthenticated' policy of the `Users & Permissions` plugin, then the 'isOwner' policy before executing the resolver.
|
policies: ['plugins.users-permissions.isAuthenticated', 'isOwner'], // Apply the 'isAuthenticated' policy of the `Users & Permissions` plugin, then the 'isOwner' policy before executing the resolver.
|
||||||
},
|
},
|
||||||
posts: {
|
restaurants: {
|
||||||
description: 'Return a list of posts', // Add a description to the query.
|
description: 'Return a list of restaurants', // Add a description to the query.
|
||||||
deprecated: 'This query should not be used anymore. Please consider using postsByAuthor instead.'
|
deprecated: 'This query should not be used anymore. Please consider using restaurantsByChef instead.'
|
||||||
},
|
},
|
||||||
postsByAuthor: {
|
restaurantsByChef: {
|
||||||
description: 'Return the posts published by the author',
|
description: 'Return the restaurants open by the chef',
|
||||||
resolver: 'Post.findByAuthor'
|
resolver: 'Restaurant.findByChef'
|
||||||
},
|
},
|
||||||
postsByTags: {
|
restaurantsByCategories: {
|
||||||
description: 'Return the posts published by the author',
|
description: 'Return the restaurants open by the category',
|
||||||
resolverOf: 'Post.findByTags', // Will apply the same policy on the custom resolver as the controller's action `findByTags`.
|
resolverOf: 'Restaurant.findByCategories', // Will apply the same policy on the custom resolver as the controller's action `findByCategories`.
|
||||||
resolver: (obj, options, ctx) => {
|
resolver: (obj, options, ctx) => {
|
||||||
// ctx is the context of the Koa request.
|
// ctx is the context of the Koa request.
|
||||||
await strapi.controllers.posts.findByTags(ctx);
|
await strapi.controllers.restaurants.findByCategories(ctx);
|
||||||
|
|
||||||
return ctx.body.posts || `There is no post.`;
|
return ctx.body.restaurants || `There is no restaurant.`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
attachPostToAuthor: {
|
attachRestaurantToChef: {
|
||||||
description: 'Attach a post to an author',
|
description: 'Attach a restaurant to an chef',
|
||||||
policies: ['plugins.users-permissions.isAuthenticated', 'isOwner'],
|
policies: ['plugins.users-permissions.isAuthenticated', 'isOwner'],
|
||||||
resolver: 'Post.attachToAuthor'
|
resolver: 'Restaurant.attachToChef'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -553,7 +549,7 @@ module.exports = {
|
|||||||
Edit the `definition` attribute in one of the `schema.graphql` files of your project by using the GraphQL Type language string.
|
Edit the `definition` attribute in one of the `schema.graphql` files of your project by using the GraphQL Type language string.
|
||||||
|
|
||||||
::: note
|
::: note
|
||||||
The easiest way is to create a new model using the CLI `strapi generate:model category --api post`, so you don't need to customise anything.
|
The easiest way is to create a new model using the CLI `strapi generate:model category --api restaurant`, so you don't need to customise anything.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -634,30 +630,30 @@ module.exports = {
|
|||||||
|
|
||||||
One of the most powerful features of GraphQL is the auto-documentation of the schema. The GraphQL plugin allows you to add a description to a type, a field and a query. You can also deprecate a field or a query.
|
One of the most powerful features of GraphQL is the auto-documentation of the schema. The GraphQL plugin allows you to add a description to a type, a field and a query. You can also deprecate a field or a query.
|
||||||
|
|
||||||
**Path —** `./api/post/models/Post.settings.json`.
|
**Path —** `./api/restaurant/models/Restaurant.settings.json`.
|
||||||
|
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
"connection": "default",
|
"connection": "default",
|
||||||
"info": {
|
"info": {
|
||||||
"description": "The Post type description"
|
"description": "The Restaurant type description"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"timestamps": true
|
"timestamps": true
|
||||||
},
|
},
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"title": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The title of the post",
|
"description": "The name of the restaurant",
|
||||||
"deprecated": "We are not using the title anymore, it is auto-generated thanks to our powerful AI"
|
"deprecated": "We are not using the name anymore, it is auto-generated thanks to our powerful AI"
|
||||||
},
|
},
|
||||||
"content": {
|
"description": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"description": "The content of the post."
|
"description": "The description of the restaurant."
|
||||||
},
|
},
|
||||||
"published": {
|
"open": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Is the post published or not. Yes = true."
|
"description": "Is the restaurant open or not. Yes = true."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -669,21 +665,21 @@ It might happen that you want to add a description to a query or deprecate it. T
|
|||||||
The `schema.graphql` file has to be placed into the config folder of each API `./api/*/config/schema.graphql` or plugin `./extensions/*/config/schema.graphql`.
|
The `schema.graphql` file has to be placed into the config folder of each API `./api/*/config/schema.graphql` or plugin `./extensions/*/config/schema.graphql`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
**Path —** `./api/post/config/schema.graphql`.
|
**Path —** `./api/restaurant/config/schema.graphql`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
module.exports = {
|
module.exports = {
|
||||||
resolver: {
|
resolver: {
|
||||||
Query: {
|
Query: {
|
||||||
posts: {
|
restaurants: {
|
||||||
description: 'Return a list of posts', // Add a description to the query.
|
description: 'Return a list of restaurants', // Add a description to the query.
|
||||||
deprecated:
|
deprecated:
|
||||||
'This query should not be used anymore. Please consider using postsByAuthor instead.', // Deprecate the query and explain the reason why.
|
'This query should not be used anymore. Please consider using restaurantsByChef instead.', // Deprecate the query and explain the reason why.
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
createPost: {
|
createRestaurant: {
|
||||||
description: 'Create a new post',
|
description: 'Create a new restaurant',
|
||||||
deprecated: 'Please use the dashboard UI instead',
|
deprecated: 'Please use the dashboard UI instead',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -699,8 +695,8 @@ Sometimes a query needs to be only accessible to authenticated user. To handle t
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
resolver: {
|
resolver: {
|
||||||
Query: {
|
Query: {
|
||||||
posts: {
|
restaurants: {
|
||||||
description: 'Return a list of posts',
|
description: 'Return a list of restaurants',
|
||||||
policies: [
|
policies: [
|
||||||
'plugins.users-permissions.isAuthenticated',
|
'plugins.users-permissions.isAuthenticated',
|
||||||
'isOwner',
|
'isOwner',
|
||||||
@ -709,8 +705,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
createPost: {
|
createRestaurant: {
|
||||||
description: 'Create a new post',
|
description: 'Create a new restaurant',
|
||||||
policies: [
|
policies: [
|
||||||
'plugins.users-permissions.isAuthenticated',
|
'plugins.users-permissions.isAuthenticated',
|
||||||
'global.logging',
|
'global.logging',
|
||||||
@ -721,42 +717,42 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, the policy `isAuthenticated` located in the `users-permissions` plugin will be executed first. Then, the `isOwner` policy located in the `Post` API `./api/post/config/policies/isOwner.js`. Next, it will execute the `logging` policy located in `./config/policies/logging.js`. Finally, the resolver will be executed.
|
In this example, the policy `isAuthenticated` located in the `users-permissions` plugin will be executed first. Then, the `isOwner` policy located in the `Restaurant` API `./api/restaurant/config/policies/isOwner.js`. Next, it will execute the `logging` policy located in `./config/policies/logging.js`. Finally, the resolver will be executed.
|
||||||
|
|
||||||
::: note
|
::: note
|
||||||
There is no custom resolver in that case, so it will execute the default resolver (Post.find) provided by the Shadow CRUD feature.
|
There is no custom resolver in that case, so it will execute the default resolver (Restaurant.find) provided by the Shadow CRUD feature.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Link a query or mutation to a controller action
|
### Link a query or mutation to a controller action
|
||||||
|
|
||||||
By default, the plugin will execute the actions located in the controllers that has been generated via the Content-Type Builder plugin or the CLI. For example, the query `posts` is going to execute the logic inside the `find` action in the `Post.js` controller. It might happen that you want to execute another action or a custom logic for one of your query.
|
By default, the plugin will execute the actions located in the controllers that has been generated via the Content-Type Builder plugin or the CLI. For example, the query `restaurants` is going to execute the logic inside the `find` action in the `Restaurant.js` controller. It might happen that you want to execute another action or a custom logic for one of your query.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
module.exports = {
|
module.exports = {
|
||||||
resolver: {
|
resolver: {
|
||||||
Query: {
|
Query: {
|
||||||
posts: {
|
restaurants: {
|
||||||
description: 'Return a list of posts by author',
|
description: 'Return a list of restaurants by chef',
|
||||||
resolver: 'Post.findByAuthor',
|
resolver: 'Restaurant.findByChef',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
createPost: {
|
createRestaurant: {
|
||||||
description: 'Create a new post',
|
description: 'Create a new restaurant',
|
||||||
resolver: 'Post.customCreate',
|
resolver: 'Restaurant.customCreate',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
In this example, it will execute the `findByAuthor` action of the `Post` controller. It also means that the resolver will apply on the `posts` query the permissions defined on the `findByAuthor` action (through the administration panel).
|
In this example, it will execute the `findByChef` action of the `Restaurant` controller. It also means that the resolver will apply on the `restaurants` query the permissions defined on the `findByChef` action (through the administration panel).
|
||||||
|
|
||||||
::: note
|
::: note
|
||||||
The `obj` parameter is available via `ctx.params` and the `options` are available via `ctx.query` in the controller's action.
|
The `obj` parameter is available via `ctx.params` and the `options` are available via `ctx.query` in the controller's action.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
The same process is also applied for the `createPost` mutation. It will execute the `customCreate` action of the `Post` controller.
|
The same process is also applied for the `createRestaurant` mutation. It will execute the `customCreate` action of the `Restaurant` controller.
|
||||||
|
|
||||||
::: note
|
::: note
|
||||||
The `where` parameter is available via `ctx.params` and the `data` are available via `ctx.request.body` in the controller's action.
|
The `where` parameter is available via `ctx.params` and the `data` are available via `ctx.request.body` in the controller's action.
|
||||||
@ -768,28 +764,28 @@ The `where` parameter is available via `ctx.params` and the `data` are available
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
resolver: {
|
resolver: {
|
||||||
Query: {
|
Query: {
|
||||||
posts: {
|
restaurants: {
|
||||||
description: 'Return a list of posts by author',
|
description: 'Return a list of restaurants by chef',
|
||||||
resolver: (obj, options, { context }) => {
|
resolver: (obj, options, { context }) => {
|
||||||
// You can return a raw JSON object or a promise.
|
// You can return a raw JSON object or a promise.
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
title: 'My first blog post',
|
name: 'My first blog restaurant',
|
||||||
content: 'Whatever you want...'
|
description: 'Whatever you want...'
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
updatePost: {
|
updateRestaurant: {
|
||||||
description: 'Update an existing post',
|
description: 'Update an existing restaurant',
|
||||||
resolver: (obj, options, { context }) => {
|
resolver: (obj, options, { context }) => {
|
||||||
// The `where` and `data` parameters passed as arguments
|
// The `where` and `data` parameters passed as arguments
|
||||||
// of the GraphQL mutation are available via the `context` object.
|
// of the GraphQL mutation are available via the `context` object.
|
||||||
const where = context.params;
|
const where = context.params;
|
||||||
const data = context.request.body;
|
const data = context.request.body;
|
||||||
|
|
||||||
return await strapi.api.post.services.post.addPost(data, where);
|
return await strapi.api.restaurant.services.restaurant.addRestaurant(data, where);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -807,28 +803,28 @@ It might happen that you want to apply our permissions layer on a query. That's
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
resolver: {
|
resolver: {
|
||||||
Query: {
|
Query: {
|
||||||
posts: {
|
restaurants: {
|
||||||
description: 'Return a list of posts by author',
|
description: 'Return a list of restaurants by chef',
|
||||||
resolverOf: 'Post.find', // Will apply the same policy on the custom resolver as the controller's action `find` located in `Post.js`.
|
resolverOf: 'Restaurant.find', // Will apply the same policy on the custom resolver as the controller's action `find` located in `Restaurant.js`.
|
||||||
resolver: (obj, options, context) => {
|
resolver: (obj, options, context) => {
|
||||||
// You can return a raw JSON object or a promise.
|
// You can return a raw JSON object or a promise.
|
||||||
|
|
||||||
return [{
|
return [{
|
||||||
title: 'My first blog post',
|
name: 'My first blog restaurant',
|
||||||
content: 'Whatever you want...'
|
description: 'Whatever you want...'
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
updatePost: {
|
updateRestaurant: {
|
||||||
description: 'Update an existing post',
|
description: 'Update an existing restaurant',
|
||||||
resolverOf: 'Post.update', // Will apply the same policy on the custom resolver than the controller's action `update` located in `Post.js`.
|
resolverOf: 'Restaurant.update', // Will apply the same policy on the custom resolver than the controller's action `update` located in `Restaurant.js`.
|
||||||
resolver: (obj, options, { context }) => {
|
resolver: (obj, options, { context }) => {
|
||||||
const where = context.params;
|
const where = context.params;
|
||||||
const data = context.request.body;
|
const data = context.request.body;
|
||||||
|
|
||||||
return await strapi.api.post.services.post.addPost(data, where);
|
return await strapi.api.restaurant.services.restaurant.addRestaurant(data, where);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -843,14 +839,14 @@ To do that, we need to use the `schema.graphql` like below:
|
|||||||
```js
|
```js
|
||||||
module.exports = {
|
module.exports = {
|
||||||
type: {
|
type: {
|
||||||
Post: false // The Post type won't be "queriable" or "mutable".
|
Restaurant: false // The Restaurant type won't be "queriable" or "mutable".
|
||||||
}
|
}
|
||||||
resolver: {
|
resolver: {
|
||||||
Query: {
|
Query: {
|
||||||
posts: false // The `posts` query will no longer be in the GraphQL schema.
|
restaurants: false // The `restaurants` query will no longer be in the GraphQL schema.
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
createPost: false,
|
createRestaurant: false,
|
||||||
deletePOst: false
|
deletePOst: false
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user