Skip to main content

09 — GraphQL

Goal: Add @webda/graphql, configure the GraphQLService, and query the blog domain with GraphQL.

Files touched: package.json (add @webda/graphql), webda.config.json (add GraphQLService).

Concepts: schema generated from the models, query and mutation naming, the same permissions as REST.

Walkthrough​

1. Install @webda/graphql​

npm install @webda/graphql

2. Add the GraphQLService to webda.config.json​

webda.config.json (services excerpt)
{
"services": {
"GraphQLService": {
"type": "Webda/GraphQLService"
}
}
}

The service serves /graphql on the existing HttpServer and builds its schema from every model DomainService exposes. No other configuration is needed. (npm create @webda my-blog -- --transports rest,graphql adds the same entry.)

3. Rebuild and restart​

npm run debug # or npm run serve

With npm run debug, open http://localhost:18080/graphql in a browser for the GraphiQL explorer (exposeGraphiQL defaults to on in debug mode).

4. What the schema contains​

For each model, with Post as the example:

GraphQLKindSame as REST
Post(slug: String)queryPost.Get
Posts(query: String) → PostQueryResultqueryPosts.Query
createPost(Post: PostInput)mutationPost.Create
updatePost(uuid: String, Post: PostInput)mutationPost.Update
deletePost(uuid: String) → { success }mutationPost.Delete
Post(slug:), Posts(query:), PostEvents(slug:), PostsEventssubscriptionrepository events
  • The single-object query takes the primary key field (Post(slug:), User(uuid:)); the update/delete mutations name the key argument uuid, whatever the key is.
  • Posts(query:) takes a WebdaQL query, like PUT /posts, and returns results and continuationToken.
  • Me returns the logged-in user.
  • The root types are named Query, Mutations and Subscription.
  • Model and service @Operations (register, login, publish, Publisher.*) are not part of the GraphQL schema: call them over REST or gRPC.

Verify​

Introspect the root types:

curl -s -X POST http://localhost:18080/graphql -H "Content-Type: application/json" \
-d '{"query":"{ __schema { queryType { name } mutationType { name } subscriptionType { name } } }"}' | jq -c
{
"data": {
"__schema": {
"queryType": { "name": "Query" },
"mutationType": { "name": "Mutations" },
"subscriptionType": { "name": "Subscription" }
}
}
}

List users (public profiles, as over REST):

curl -s -X POST http://localhost:18080/graphql -H "Content-Type: application/json" \
-d '{"query":"{ Users { results { uuid username name } } }"}' | jq
{
"data": {
"Users": {
"results": [{ "uuid": "096a4874-4b98-4001-a108-fac5d3e1efd3", "username": "alice", "name": "Alice Smith" }]
}
}
}

Query published posts — the WebdaQL query goes in the query argument:

curl -s -X POST http://localhost:18080/graphql -H "Content-Type: application/json" \
-d "{\"query\":\"{ Posts(query: \\\"status = 'published' ORDER BY title\\\") { results { slug title author } } }\"}" | jq

Read one post by its slug:

curl -s -X POST http://localhost:18080/graphql -H "Content-Type: application/json" \
-d '{"query":"{ Post(slug: \"hello-world\") { slug title content status viewCount } }"}' | jq .data.Post

Mutations follow the model permissions. Anonymous, createTag is refused:

curl -s -X POST http://localhost:18080/graphql -H "Content-Type: application/json" \
-d '{"query":"mutation { createTag(Tag: {slug: \"graphql\", name: \"GraphQL\"}) { slug name } }"}' | jq -c .errors
[
{
"message": "Permission denied",
"locations": [{ "line": 1, "column": 12 }],
"path": ["createTag"],
"extensions": { "code": "PERMISSION_DENIED" }
}
]

With the session cookie (-b cookies.txt), the same request returns the tag. Update and delete work the same way:

curl -s -b cookies.txt -X POST http://localhost:18080/graphql -H "Content-Type: application/json" \
-d '{"query":"mutation { updatePost(uuid: \"hello-world\", Post: {title: \"Hello GraphQL\"}) { slug title } }"}' | jq

Errors are reported in errors[].extensions.code: NOT_FOUND for a missing (or unreadable) object, PERMISSION_DENIED when canAct refuses.

See the @webda/graphql module for subscriptions and the service parameters.

What's next​

→ 10 — gRPC