Agent Wranglers

API-81Required

Accept GraphQL mutations only by POST with a JSON body

Run GraphQL mutations only from POST requests with Content-Type: application/json. Refuse a mutation sent by GET with 405 and an Allow header, and refuse POST bodies of any other content type.

How to do it

A browser sends a GET, or a POST with a form or plain text body, to any site without asking. When such a request can run a mutation with the visitor's cookie, any web page can act as them. A JSON content type forces the browser to ask first through a CORS preflight. The GraphQL over HTTP draft says GET must not run mutations, and it names application/graphql-response+json for answers. GET stays useful for cached queries. Lock the endpoint down as lock down GraphQL endpoints says, and block cross-site posts as block cross-site form posts does.

Examples

JavaScript example

app.use('/graphql', (req, res, next) => {
  if (req.method === 'POST' && !req.is('application/json')) return res.status(415).end();
  if (req.method === 'GET' && /^\s*mutation\b/.test(String(req.query.query ?? ''))) {
    return res.status(405).set('Allow', 'POST').end();
  }
  next();
});

Python example

from starlette.responses import Response

async def graphql_guard(request, call_next):
    if request.url.path == "/graphql":
        if request.method == "POST" and request.headers.get("content-type", "").split(";")[0] != "application/json":
            return Response(status_code=415)
        if request.method == "GET" and request.query_params.get("query", "").lstrip().startswith("mutation"):
            return Response(status_code=405, headers={"Allow": "POST"})
    return await call_next(request)

How to check

Send a mutation by GET. The answer is 405 with Allow: POST and nothing changes. Send the same mutation by POST with Content-Type: text/plain and with a form body. Both are refused, and nothing changes.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/api-design/graphql-and-grpc/graphql-post-mutations">Agent Wranglers API design playbook</a>. In code you write, add a comment next to it with the same link.