Skip to main content

graphql

@webda/graphql​

Auto-generates a fully functional GraphQL API — queries, mutations, and subscriptions — from your Webda domain models. Zero hand-written SDL required.

When to use it​

Add @webda/graphql when you want a GraphQL endpoint alongside (or instead of) the REST endpoint. It reads your model graph from @webda/compiler's model manifest and builds the GraphQL schema dynamically. Subscriptions over WebSocket (graphql-ws) are enabled by default.

Install​

npm install @webda/graphql

Configuration​

Add the GraphQLService to your webda.config.json:

{
"services": {
"GraphQLService": {
"type": "Webda/GraphQLService",
"url": "/graphql",
"exposeGraphiQL": true,
"globalSubscription": true
}
}
}

Auto-generated operations​

For every exposed model (e.g. Post) the service generates:

OperationTypeDescription
Post(uuid)QueryFetch one by primary key
Posts(query, limit, offset)QueryList / filter all
createPost(Post: PostInput)MutationCreate new instance
updatePost(uuid, Post: PostInput)MutationReplace by primary key
deletePost(uuid)MutationDelete by primary key
PostEventsSubscriptionStream events for all posts
PostEvent(uuid)SubscriptionStream events for a specific post

Custom @Operation() methods on the model become additional mutations or queries automatically.

Quick usage​

# Query all published posts
{ Posts { results { slug title status viewCount } } }

# Create a post
mutation {
createPost(Post: {
title: "Hello World"
slug: "hello-world"
content: "My first post content with enough text."
status: "draft"
viewCount: 0
}) {
slug
title
}
}

Subscriptions (graphql-ws)​

subscription {
PostEvents {
Created { object { slug title } }
Updated { object { slug title } }
Deleted { object_id }
}
}

The subscription server uses graphql-ws over WebSocket (same port as the HTTP server when TLS is enabled via autoTls).

Model mutations are the model operations​

create<Model>, update<Model> and delete<Model> call the operation a DomainService registers for the model (<Model>.Create, .Update, .Delete), the one REST, gRPC and MCP call. So a mutation gets the same permission checks in the same order, the same sanitization, the same validation against the model schema (@pattern, @minLength, @maximum, @format, enums...), the same events (the audit log sees it) and the same transaction. A refused input is a BAD_USER_INPUT error whose message is the REST one and whose extensions.details.errors holds the validation errors:

{
"message": "Product.Create InvalidInput: /name must match pattern \"^[A-Z][A-Za-z &]*$\"",
"extensions": { "code": "BAD_USER_INPUT", "details": { "errors": [{ "instancePath": "/name", "keyword": "pattern" }] } }
}

A model no DomainService exposes has no such operation: its mutations are applied by the GraphQL service, with the same sanitization and the same schema validation, but without the operation events.

Input sanitization​

The request is not sanitized as a whole: every argument is, once GraphQL has read it, so a value sent as a variable and the same value written in the document are treated the same way, over HTTP and over WebSocket.

ArgumentSanitized
The input of a model mutation (createPost(Post: ...), updatePost)With the @sanitize modes of the model, like the body of the same REST operation: an attribute stores the same value on every transport
The arguments of an operation (service operations, model and behavior actions, subscriptions on a streamed operation)From the operation input schema, like the body of the same REST operation
WebdaQL query and filter argumentsNot sanitized: the query is parsed, and its string literals are sanitized per attribute, like on the other transports (see the model documentation, Text sanitization, Queries)
Anything else: keys (uuid), the arguments of a field another service addsAs text: tags removed, &, <, > escaped

The keys that reach a prototype (__proto__, constructor, prototype) are removed from every argument. A field added through the Webda.GraphQL.Schema event gets the same treatment without doing anything: no argument reaches a resolver unsanitized. A resolver that must sanitize an argument itself names it in the field extensions (webdaRawArgs: ["name"]).

The endpoint executes documents sent by POST (one per request, no batching) and over the WebSocket protocol; a GET only serves GraphiQL.

GraphiQL​

Set exposeGraphiQL: true (the default) to enable the in-browser GraphiQL IDE at /graphql. Useful for development and introspection.

Parameters​

ParameterDefaultDescription
url/graphqlHTTP endpoint path
exposeGraphiQLtrueServe the GraphiQL explorer UI
exposeMetrueExpose { me { ... } } query for current user
globalSubscriptiontrueExpose aggregate AggregateSubscriptions subscription
userModelUserShort name of the User model for me query

See also​