Nvim-lspconfig: Can't get graphql LSP working

I’m struggling to get the graphql LSP working. I’ve followed the instructions and have

  • npm install -g graphql-language-service-cli
  • Enabled the LSP in my nvim config just like my other (working) LSP servers: lspconfig.graphql.setup {}
  • Installed the graphql package in my project
  • Created a graphql.config.js in my project

I’m following the GraphQL Apollo Tutorial here, so my project is very simple. The schema file is in src/schema.gql.

So the graphql.config.js only contains:

module.exports = {
  schema: "./src/schema.gql"
}

When I edit schema.gql and check LspInfo it tells me:

0 client(s) attached to this buffer:

Other clients that match the filetype: graphql

Config: graphql
filetypes: graphql, typescriptreact, javascriptreact
root directory: /home/repos/local/graphql-tutorial-hackernews
cmd: graphql-lsp server -m stream
cmd is executable: true
autostart: true
custom handlers:

So the server gets started and root dir is fine but it is not attached to the buffer.

I’ve checked the LSP debug log and see:

[DEBUG][2022-06-02 09:54:23] .../vim/lsp/rpc.lua:347	"rpc.send"	{  id = 1,  jsonrpc = "2.0",  method = "initialize",  params = {    capabilities = {      callHierarchy = {        dynamicRegistration = false      },      textDocument = {        codeAction = {          codeActionLiteralSupport = {            codeActionKind = {              valueSet = { "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }            }          },          dataSupport = true,          dynamicRegistration = false,          resolveSupport = {            properties = { "edit" }          }        },        completion = {          completionItem = {            commitCharactersSupport = true,            deprecatedSupport = true,            documentationFormat = { "markdown", "plaintext" },            insertReplaceSupport = true,            labelDetailsSupport = true,            preselectSupport = true,            resolveSupport = {              properties = { "documentation", "detail", "additionalTextEdits" }            },            snippetSupport = true,            tagSupport = {              valueSet = { 1 }            }          },          completionItemKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }          },          contextSupport = false,          dynamicRegistration = false        },        declaration = {          linkSupport = true        },        definition = {          linkSupport = true        },        documentHighlight = {          dynamicRegistration = false        },        documentSymbol = {          dynamicRegistration = false,          hierarchicalDocumentSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        hover = {          contentFormat = { "markdown", "plaintext" },          dynamicRegistration = false        },        implementation = {          linkSupport = true        },        publishDiagnostics = {          relatedInformation = true,          tagSupport = {            valueSet = { 1, 2 }          }        },        references = {          dynamicRegistration = false        },        rename = {          dynamicRegistration = false,          prepareSupport = true        },        signatureHelp = {          dynamicRegistration = false,          signatureInformation = {            activeParameterSupport = true,            documentationFormat = { "markdown", "plaintext" },            parameterInformation = {              labelOffsetSupport = true            }          }        },        synchronization = {          didSave = true,          dynamicRegistration = false,          willSave = false,          willSaveWaitUntil = false        },        typeDefinition = {          linkSupport = true        }      },      window = {        showDocument = {          support = false        },        showMessage = {          messageActionItem = {            additionalPropertiesSupport = false          }        },        workDoneProgress = true      },      workspace = {        applyEdit = true,        configuration = true,        symbol = {          dynamicRegistration = false,          hierarchicalWorkspaceSymbolSupport = true,          symbolKind = {            valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }          }        },        workspaceEdit = {          resourceOperations = { "rename", "create", "delete" }        },        workspaceFolders = true      }    },    clientInfo = {      name = "Neovim",      version = "0.7.0"    },    initializationOptions = vim.empty_dict(),    processId = 680034,    rootPath = "/home/repos/local/graphql-tutorial-hackernews",    rootUri = "file:///home/repos/local/graphql-tutorial-hackernews",    trace = "off",    workspaceFolders = { {        name = "/home/repos/local/graphql-tutorial-hackernews",        uri = "file:///home/repos/local/graphql-tutorial-hackernews"      } }  }}
[ERROR][2022-06-02 09:54:24] .../vim/lsp/rpc.lua:420	"rpc"	"graphql-lsp"	"stderr"	'2.6.2022, 09:54:24 [3] (pid: 680036) graphql-language-service-usage-logs: {"type":"usage","messageType":"initialize"}\n'
[DEBUG][2022-06-02 09:54:24] .../vim/lsp/rpc.lua:454	"rpc.receive"	{  id = 1,  jsonrpc = "2.0",  result = {    capabilities = {      completionProvider = {        resolveProvider = true,        triggerCharacters = { " ", ":", "$", "\n", " ", "(", "@" }      },      definitionProvider = true,      documentSymbolProvider = true,      hoverProvider = true,      textDocumentSync = 1,      workspace = {        workspaceFolders = {          changeNotifications = true,          supported = true        }      },      workspaceSymbolProvider = true    }  }}
[DEBUG][2022-06-02 09:54:24] .../vim/lsp/rpc.lua:347	"rpc.send"	{  jsonrpc = "2.0",  method = "initialized",  params = vim.empty_dict()}
[DEBUG][2022-06-02 09:54:24] .../lua/vim/lsp.lua:982	"LSP[graphql]"	"server_capabilities"	{  completionProvider = {    resolveProvider = true,    triggerCharacters = { " ", ":", "$", "\n", " ", "(", "@" }  },  definitionProvider = true,  documentSymbolProvider = true,  hoverProvider = true,  textDocumentSync = 1,  workspace = {    workspaceFolders = {      changeNotifications = true,      supported = true    }  },  workspaceSymbolProvider = true}
[INFO][2022-06-02 09:54:24] .../lua/vim/lsp.lua:983	"LSP[graphql]"	"initialized"	{  resolved_capabilities = {    call_hierarchy = false,    code_action = false,    code_lens = false,    code_lens_resolve = false,    completion = true,    declaration = false,    document_formatting = false,    document_highlight = false,    document_range_formatting = false,    document_symbol = true,    execute_command = false,    find_references = false,    goto_definition = true,    hover = true,    implementation = false,    rename = false,    signature_help = false,    signature_help_trigger_characters = {},    text_document_did_change = 1,    text_document_open_close = true,    text_document_save = true,    text_document_save_include_text = false,    text_document_will_save = false,    text_document_will_save_wait_until = false,    type_definition = false,    workspace_folder_properties = {      changeNotifications = true,      supported = true    },    workspace_symbol = true  }}
[DEBUG][2022-06-02 09:54:24] .../vim/lsp/rpc.lua:347	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didOpen",  params = {    textDocument = {      languageId = "graphql",      text = "type Query {\n  info: String!\n  feed: [Link!]!\n}\n\ntype Mutation {\n  post(url: String!, description: String!): Link!,\n  signup(email: String!, password: String!, name: String!): AuthPayload\n  login(email: String!, password: String!): AuthPayload\n}\n\ntype Link {\n  id: ID!\n  description: String!\n  url: String!\n  postedBy: User\n}\n\ntype AuthPayload {\n  token: String\n  user: User\n}\n\ntype User {\n  id: ID!\n  name: String!\n  email: String!\n  links: [Link!]!\n}\n\ntype Subscription {\n  newLink: Link\n}\n",      uri = "file:///home/repos/local/graphql-tutorial-hackernews/src/schema.gql",      version = 0    }  }}

So there’s an ERROR message but I can’t really make sense of it.

At this point I’m stuck as the config LGTM but it still doesn’t connect.

Any ideas what else I could try?

Hmm, is no one using graphql with neovim? Or is there a better place to ask these kind of questions?

I do use it, but I just installed it globally and it kinda works

Thanks. This is weird. I really tried with the most simple setup and the LSP server just won’t attach. Do you have any special entries in your graphql.config.js? Could you maybe do a quick test on a fresh project?

It would really help if neovim would give a little more details with :LspInfo when a LSP server was started but not attached. What does that even mean?

Probably this is the problem? You have .js file, but server only attaches to these three filetypes.

You might want to pass a table to the setup function and add javascript to it.
See default options

Not really. See my post above, I try to edit schema.gql and filetype is also correctly detected as graphql.

Do you have/know a fresh project/template I can use ?

A simple project should be easy to setup:

mkdir demo
cd demo
npm init -y
npm install --save graphql
echo "schema: 'schema.gql'" > .graphqlrc
nvim schema.gql

Then check with :LspInfo whether the LSP server is connected. In my case it’s not.

Does this work for you? If not, maybe I’m missing some important config in the .graphqlrc? I also tried to add documents: '*.gql' to the config but no difference.

Note that it should not matter if we use .graphqlrc or graphql.config.js (see here). Both don’t work for me anyway.

[Edit: Added missing npm init -y]

Nope, it doesn’t work:

Maybe I was happy enough with just higlighting

In ts files it is not attaching either (but it is available):

And some errors:

Ok, thanks for trying and the feedback. I still wonder if anyone got a working setup.

Maybe I should open a neovim issue. But then again I have the strong feeling I’m missing something obvious.

I’m not sure if you have this working now, but you also need the graphql-language-service-server installed in addition to the cli, so
npm i -g graphql-language-service-server
Then it should work for you.

It’s been a while so before I tried your suggestion I did a quick test with my example above and now it seems to work. So probably something was fixed meanwhile.

But thanks nonetheless.

Oh cool glad it’s now working for you.
You need both graphql-language-service-cli and graphql-language-service-server installed globally, which isn’t documented in the LSP Config
So you only had the cli installed from the looks of things, I had the same issue when setting up.

You need both graphql-language-service-cli and graphql-language-service-server installed globally

Hmm, are you sure? I don’t have graphql-language-service-server installed and it seems to work. I did no thorough tests, though, only checked with :LspInfo if LSP is attached. But as I use a code first approach now I’ve not worked on graphql files lately so not sure what to even expect from LSP support.

But if it’s really required, we should update the instructions you linked.

Yeah, the lsp will show attached and running but without the graphql-language-service-server available you won’t actually get any lsp features like go to definition, formatting, documentation, linting and such.
While it doesn’t mention it in the LSP config documentation, it does mention it in the graphql-language-service-cli documentation, it’s just not obvious.
I too don’t really use graphql files but some people who use my Neovim config do, so I’m only aware of it from helping them get it working.

Ok, thanks but I see that graphql-language-service-server is already a dependency of the ...-cli package:

I also find it locally in my global node_modules under /usr/local/lib/node_modules/graphql-language-service-cli/node_modules/graphql-language-service-server.

So I’d still say, you should not need to install this additionally.

Do you maybe have any pointers where this extra requirement is mentioned? If this extra step is really required we should update lspconfig instructions and also the install instructions for graphql-language-service-cli itself.

Oh yes.
All I really know is we had to install it in addition to the cli before lsp would work.
It’s mentioned on the last line of this paragraph
Which takes you to this package.

Actually I just uninstalled the service-server and checked nvim lsp and didn’t have it available.
I then updated the global installation of the service-cli and tested again in nvim and I do have lsp available.
So looks like something has changed in the cli.
Sorry for any confusion.