the default JSONDiagnosticOptions
from monaco-editor
's json
mode - to use when applying variablesJSON.
some examples of settings to provide here:
allowComments: true
enables jsonc editingvalidateSchema: 'warning'
trailingComments
is error
by default, and can be warning
or ignore
{languages.json.DiagnosticsOptions}whilst editing operations, alongside graphql validation, generate json schema for variables to validate json schema models
For the monaco-graphql
language worker, these must be specified
in a custom webworker. see the readme.
Custom validation rules following graphql
ValidationRule
signature
External fragments to be used with completion and validation
Should field leafs be automatically expanded & filled on autocomplete?
NOTE: this can be annoying with required arguments
Custom options passed to parse
, whether graphql
parse by default or custom parser
Provide a parser that matches graphql
parse()
signature
Used for internal document parsing operations
for autocompletion and hover, graphql-language-service-parser
is used via graphql-language-service-interface
Take a variety of schema inputs common for the language worker, and transform them
to at least a schema
if not other easily available implementations
An array of schema configurations from which to match files for language features You can provide many formats, see the config for details!
Configuration to initialize the editor with
custom (experimental) settings for autocompletion behaviour
custom settings for diagnostics (validation)
provide prettier formatting options as prettierConfig.<option>
Generic monaco language mode options, same as for the official monaco json mode
Specify array of SchemaConfig
items used to initialize the GraphQLWorker
if available.
You can also api.setSchemaConfig()
after instantiating the mode.
Inspired by the monaco-json
schema object in DiagnosticSettings["schemas"]
,
which we use :)
You have many schema format options to provide, choose one!
For large schemas, try different formats to see what is most efficient for you.
provide custom options when using buildClientSchema
, buildASTSchema
, etc
JSON schemas ued for custom scalars
A GraphQL DocumentNode AST
An SDL document string
An array of URIs or globs to associate with this schema in the language worker
Uses picomatch
which supports many common expressions except brackets
Only necessary if you provide more than one schema, otherwise it defaults to the sole schema
A parsed JSON literal of the introspection results
A stringified introspection JSON result
A GraphQLSchema instance
A unique uri string for this schema. Model data will eventually be set for this URI for definition lookup
This schema loader is focused on performance for the monaco worker runtime We favor taking in stringified schema representations as they can be used to communicate Across the main/webworker process boundary
Send the most minimal string representation to the worker for language service instantiation
Initialize the mode & worker synchronously with provided configuration
Monaco and Vscode have slightly different ideas of marker severity. for example, vscode has Error = 1, whereas monaco has Error = 8. this takes care of that
the matching marker severity level on monaco's terms
Generated using TypeDoc
Changelog | API Docs | Discord Channel
GraphQL language plugin for the Monaco Editor. You can use it to build vscode/codespaces-like web or desktop IDEs using whatever frontend javascript libraries or frameworks you want, or none!
Features
It provides the following features while editing GraphQL files:
fileMatch
expressionsmonaco-editor
basic languages supportUsage
For now, we use
language
id ofgraphql
until we can ensure we can dovetail nicely with the officialgraphql
language ID.To use with webpack, here is an example to get you started:
Sync Example
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { initializeMode } from 'monaco-graphql/initializeMode'; // `monaco-graphql/esm/initializeMode` still works // you can also configure these using the webpack or vite plugins for `monaco-editor` import GraphQLWorker from 'worker-loader!monaco-graphql/esm/graphql.worker'; // instantiates the worker & language features with the schema! const MonacoGraphQLAPI = initializeMode({ schemas: [ { schema: myGraphqlSchema as GraphQLSchema, // anything that monaco.URI.from() is compatible with uri: 'https://my-schema.com', uri: '/my-schema.graphql', // match the monaco file uris for this schema. // accepts specific uris and anything `picomatch` supports. // (everything except bracket regular expressions) fileMatch: ['**/*.graphql'], // note: not sure if ^ works if the graphql model is using urls for uris? }, ], }); window.MonacoEnvironment = { getWorker(_workerId: string, label: string) { if (label === 'graphql') { return new GraphQLWorker(); } // if you are using vite or webpack plugin, it will be found here return new Worker('editor.worker.js'); }, }; monaco.editor.create(document.getElementById('someElementId'), { value: 'query { }', language: 'graphql', formatOnPaste: true, });
Lazy Example
The existing API works as before in terms of instantiating the schema. To avoid manually calling getWorker(), you can use the monaco editor plugins for webpack or vite (see examples, and below)
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; // enables our language worker right away, despite no schema import 'monaco-graphql'; // you can also configure these using the webpack or vite plugins for `monaco-editor` import GraphQLWorker from 'worker-loader!monaco-graphql/esm/graphql.worker'; // lazily invoke the api config methods whenever we want! monaco.languages.graphql.setSchemaConfig([ { schema: myGraphqlSchema as GraphQLSchema, // anything that monaco.URI.from() is compatible with uri: 'https://my-schema.com', uri: '/my-schema.graphql', // match the monaco file uris for this schema. // accepts specific uris and anything `picomatch` supports. // (everything except bracket regular expressions) fileMatch: ['**/*.graphql'], // note: not sure if ^ works if the graphql model is using urls for uris? }, ]); window.MonacoEnvironment = { getWorker(_workerId: string, label: string) { if (label === 'graphql') { return new GraphQLWorker(); } return new Worker('editor.worker.js'); }, }; monaco.editor.create(document.getElementById('someElementId'), { value: 'query { }', language: 'graphql', formatOnPaste: true, });
This will cover the basics, making an HTTP POST with the default
introspectionQuery()
operation. To customize the entire fetcher, see advanced customization below. For more customization options, see the Monaco Editor API DocsAdvanced Usage
Variables JSON Support!
In
monaco-graphql@0.5.0
we introduced a methodgetVariablesJSONSchema
that allows you to retrieve aJSONSchema
description for the declared variables for any given set of operationsFull Sync Demo with Variables JSON
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { initializeMode } from 'monaco-graphql/initializeMode'; import GraphQLWorker from 'worker-loader!monaco-graphql/esm/graphql.worker'; window.MonacoEnvironment = { getWorker(_workerId: string, label: string) { if (label === 'graphql') { return new GraphQLWorker(); } return new Worker('editor.worker.js'); }, }; // the language service will be instantiated once the schema is available const MonacoGraphQLAPI = initializeMode({ schemas: [ { // anything that monaco.URI.from() is compatible with uri: 'https://my-schema.com', // match the monaco file uris for this schema. // accepts specific filenames and anything `picomatch` supports. fileMatch: ['**/*.graphql'], schema: myGraphqlSchema as GraphQLSchema, }, ], }); const operationModel = monaco.editor.createModel( 'query {}', 'graphql', '/operation.graphql', ); const operationEditor = monaco.editor.create( document.getElementById('someElementId'), { model: operationModel, language: 'graphql', formatOnPaste: true, }, ); const variablesSchemaUri = monaco.editor.URI.file('/variables-schema.json'); const variablesModel = monaco.editor.createModel( '{}', 'json', '/variables.json', ); const variablesEditor = monaco.editor.create( document.getElementById('someElementId'), { model: variablesModel, language: 'graphql', formatOnPaste: true, }, ); // high-level method for configuring json variables validation MonacoGraphQLAPI.setDiagnosticSettings({ validateVariablesJson: { // Urls, uris, anything that monaco.URI.from() is compatible with. // Match operation model to variables editor, // and the language service will automatically listen for changes, // and compute the json schema using the GraphQLWorker. // This is in the main process is applied to the global monaco json settings // for validation, completion and more using monaco-json's built-in JSON Schema support. [operationModel.uri.toString()]: [variablesModel.uri.toString()], }, jsonDiagnosticSettings: { allowComments: true, // allow json, parse with a jsonc parser to make requests }, }); MonacoGraphQL.setCompletionSettings({ // this auto-fills NonNull leaf fields // it used to be on by default, but is annoying when // fields contain required argument. // hoping to fix that soon! __experimental__fillLeafsOnComplete: true, });
You can also experiment with the built-in
jsonc
? (JSON syntax that allows comments and trailing commas, fortsconfig.json
, etc.) and the 3rd partymonaco-yaml
language modes for completion of other types of variable input. you can also experiment with editor methods to parse detected input into different formats, etc (yaml
pastes asjson
, etc.)You could of course prefer to generate a
jsonschema
form for variables input using a framework of your choice, instead of an editor. Enjoy!monaco-graphql/lite
You can also import a "lite" version, and manually enable only the monaco features you want!
Warning: by default, completion and other features will not work, only highlighting and validation.
import { initializeMode } from 'monaco-graphql/lite'; // enable completion import 'monaco-editor/esm/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution'; const api = initializeMode({ schemas: [ { // anything that monaco.URI.from() is compatible with uri: 'schema.graphql', // match the monaco file uris for this schema. // accepts specific filenames and anything `picomatch` supports. fileMatch: ['operation.graphql'], schema: myGraphqlSchema as GraphQLSchema, }, ], });
MonacoGraphQLAPI
(typedoc)If you call any of these API methods to modify the language service configuration at any point at runtime, the webworker will reload relevant language features.
If you
import 'monaco-graphql'
synchronously, you can access the api viamonaco.languages.graphql.api
.import 'monaco-graphql'; // now the api will be available on the `monaco.languages` global const { api } = monaco.languages.graphql;
import 'monaco-graphql'; // also this import { languages } from 'monaco-editor'; // now the api will be available on the `monaco.languages` global const { api } = languages.graphql;
Otherwise, you can, like in the sync demo above:
import { initializeMode } from 'monaco-graphql/initializeMode'; const api = initializeMode(config);
monaco.languages.graphql.api.setSchemaConfig([SchemaConfig])
same as the above, except it overwrites the entire schema config.
you can provide multiple, and use
fileMatch
to map to various uri "directory" globs or specific files.uri
can be an url or file path, anything parsable// you can load it lazily import 'monaco-graphql'; monaco.languages.graphql.api.setSchemaConfig([ { schema: GraphQLSchema, fileMatch: ['**/*.graphql'], uri: 'my-schema.graphql', }, ]);
or you can load the language features only when you have your schema
import { initializeMode } from 'monaco-graphql/initializeMode'; const schemas = [ { schema: GraphQLSchema, fileMatch: ['operations/*.graphql'], uri: 'my-schema.graphql', }, ]; const api = initializeMode({ schemas }); // add another schema. this will cause language workers and features to reset api.setSchemaConfig([ ...schemas, { introspectionJSON: myIntrospectionJSON, fileMatch: ['specific/monaco/uri.graphql'], uri: 'another-schema.graphql', }, ]);
or if you want, replace the entire configuration with a single schema. this will cause the worker to be entirely re-created and language services reset
api.setSchemaConfig([ { introspectionJSON: myIntrospectionJSON, fileMatch: ['**/*.graphql'], uri: 'my-schema.graphql', }, ]);
monaco.languages.graphql.api.setModeConfiguration()
This is where you can toggle monaco language features. all are enabled by default.
monaco.languages.graphql.api.setModeConfiguration({ documentFormattingEdits: true, completionItems: true, hovers: true, documentSymbols: true, diagnostics: true, });
monaco.languages.graphql.api.setFormattingOptions()
this accepts an object
{ prettierConfig: prettier.Options }
, which accepts any prettier option. it will not re-load the schema or language features, however the new prettier options will take effect.this method overwrites the previous configuration, and will only accept static values that can be passed between the main/worker process boundary.
monaco.languages.graphql.api.setFormattingOptions({ // if you wanna be like that prettierOptions: { tabWidth: 2, useTabs: true }, });
monaco.languages.graphql.api.setExternalFragmentDefinitions()
Append external fragments to be used by autocomplete and other language features.
This accepts either a string that contains fragment definitions, or
TypeDefinitionNode[]
monaco.languages.graphql.api.getDiagnosticOptions
monaco.languages.graphql.api.setDiagnosticSettings({ validateVariablesJson: { // Urls, uris, anything that monaco.URI.from() is compatible with. // Match operation model to variables editor, // and the language service will automatically listen for changes, // and compute the json schema using the GraphQLWorker. // This is in the main process is applied to the global monaco json settings // for validation, completion and more using monaco-json's built-in JSON Schema support. [operationModel.uri.toString()]: [variablesModel.uri.toString()], }, jsonDiagnosticSettings: { allowComments: true, // allow json, parse with a jsonc parser to make requests }, });
Bundlers
Webpack
you'll can refer to the webpack configuration in the full monaco webpack example to see how it works with webpack and the official
monaco-editor-webpack-plugin
. there is probably an easier way to configure webpackworker-loader
for this.Notes:
features
for the webpack plugin, or import them directlytypescript
as a language, there is an outstanding bug with the webpack plugin, see our next.js example for the workaround. do not specifylanguages: ['typescript']
orjavascript
Vite
You can configure vite to load
monaco-editor
json mode and even the language editor worker using the example for our modeBe sure to import additional editor features and language modes manually, as the vite plugin only allows you to specify
languageWorkers
. See the vite example to see how to add typescript supportWeb Frameworks
the plain javascript webpack example should give you a starting point to see how to implement it with
React
use-monaco
seems to support the custom language worker configuration we want, and seems to be well built! we hope to help them build theiruseEffect
ondidMount
to prevent breaking SSR.MonacoEnvironment.getWorkerUrl
which works better as an async import of your pre-build worker filesCustom Webworker (for passing non-static config to worker)
If you want to pass a custom parser and/or validation rules, it is supported, however the setup is a bit more complicated.
You can add any
LanguageServiceConfig
(typedoc) configuration options you like here tolanguageConfig
as below.This is because we can't pass non-static configuration to the existing worker programmatically, so you must import these and build the worker custom with those functions. Part of the (worthwhile) cost of crossing runtimes!
you'll want to create your own
my-graphql.worker.ts
file, and add your custom config such asschemaLoader
tocreateData
:import type { worker as WorkerNamespace } from 'monaco-editor'; // @ts-expect-error - ignore missing types import * as worker from 'monaco-editor/esm/vs/editor/editor.worker'; import { GraphQLWorker } from 'monaco-graphql/esm/GraphQLWorker'; import { myValidationRules } from './custom'; self.onmessage = () => { worker.initialize( ( ctx: WorkerNamespace.IWorkerContext, createData: monaco.languages.graphql.ICreateData, ) => { createData.languageConfig.customValidationRules = myValidationRules; return new GraphQLWorker(ctx, createData); }, ); };
then, in your application:
import EditorWorker from 'worker-loader!monaco-editor/esm/vs/editor/editor.worker'; // specify the path to your language worker import GraphQLWorker from 'worker-loader!./my-graphql.worker'; window.MonacoEnvironment = { getWorker(_workerId: string, label: string) { return label === 'graphql' ? new GraphQLWorker() : new EditorWorker(); }, };
or, if you have webpack configured for it:
window.MonacoEnvironment = { getWorkerUrl(_workerId: string, label: string) { return label === 'graphql' ? 'my-graphql.worker.js' : 'editor.worker.js'; }, };
with vite you just need:
import { defineConfig } from 'vite'; import monacoEditorPlugin from 'vite-plugin-monaco-editor'; export default defineConfig({ plugins: [ monacoEditorPlugin({ customWorker: [ { label: 'graphql', entry: 'my-graphql.worker.js', }, ], }), ], });
Monaco Editor Tips
If you are familiar with Codemirror/Atom-era terminology and features, here's some gotchas:
editor.setModelMarkers()
Avoid Bundle All
monaco-editor
's LanguagesWhile importing
monaco-editor
in your project, you silently import 83 builtin languages, such astypescript
,html
,css
,json
and others. You can found a full list of basic-languages and languages.For
monaco-graphql
, you need only 2 languages -graphql
andjson
. In versionmonaco-graphql@1.3.0
and later, you can replace allmonaco-editor
's imports withmonaco-graphql/esm/monaco-editor
to improve performance, load onlygraphql
andjson
languages, and skip loading unused languages.-import { ... } from 'monaco-editor' +import { ... } from 'monaco-graphql/esm/monaco-editor'
Catch Future Import Mistakes with ESLint
To prevent mis-importing of
monaco-editor
, you can set up defaultno-restricted-imports
rule for JavaScript projects or@typescript-eslint/no-restricted-imports
for TypeScript projects.Inspiration
microsoft/monaco-json
was our inspiration from the outset, when it was still a standalone repository. @acao actually wholesale copied many files, you could almost say it was a fork!TODO
insertText
for field and argument completion