[{"data":1,"prerenderedAt":266},["ShallowReactive",2],{"DefaultLayouten":3,"language-blog-slug-cms-driven-intellisense-in-your-code-editor-i18n-slugs":134,"language-blog-slug-en-cms-driven-intellisense-in-your-code-editor":138},{"app":4,"menu":31,"footer":66},{"githubUrl":5,"youtubeUrl":6,"linkedinUrl":7,"phoneNumber":8,"emailAddress":9,"legal":10,"addresses":20},"https:\u002F\u002Fgithub.com\u002Fvoorhoede\u002F","https:\u002F\u002Fwww.youtube.com\u002Fchannel\u002FUCzHuhQVYFRixtQN2-swcuGg","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fde-voorhoede","+31 20 2610 954","post@voorhoede.nl",[11,14,17],{"title":12,"value":13},"KvK","56017235",{"title":15,"value":16},"BTW","NL851944620B01",{"title":18,"value":19},"IBAN","NL14TRIO0320142078",[21,26],{"address":22,"city":23,"googleMapsLink":24,"postalCode":25},"Koivistokade 70","Amsterdam","https:\u002F\u002Fwww.google.com\u002Fmaps\u002Fplace\u002FDe+Voorhoede+%7C+Front-end+Development\u002F@52.396847,4.8700823,17z\u002Fdata=!3m1!4b1!4m5!3m4!1s0x47c5e21d502d2d59:0xbf570944a96ebf45!8m2!3d52.347647!4d4.8502154","1013 BB",{"address":27,"city":28,"googleMapsLink":29,"postalCode":30},"Koornmarkt 22","Delft","https:\u002F\u002Fwww.google.nl\u002Fmaps\u002Fplace\u002FKoornmarkt+22,+2611+EG+Delft\u002F@52.0093477,4.3573054,17z\u002F","2611 EG",{"title":32,"callToActions":33,"links":39},"Site Menu",[34],{"id":35,"title":36,"link":37},"163140902","Contact",{"__typename":38},"ContactRecord",[40,46,51,56,61],{"id":41,"title":42,"link":43},"163140904","Impact",{"__typename":44,"slug":45},"PageRecord","impact",{"id":47,"title":48,"link":49},"163140905","Services",{"__typename":50},"ServiceOverviewRecord",{"id":52,"title":53,"link":54},"163140906","Cases",{"__typename":55},"CaseOverviewRecord",{"id":57,"title":58,"link":59},"163140908","About us",{"__typename":44,"slug":60},"about-us",{"id":62,"title":63,"link":64},"d6WdFJq2SOuc3dWtpibbXQ","Work at",{"__typename":44,"slug":65},"work-at",{"links":67,"copyrightTitle":93,"copyrightLabel":94,"copyrightLink":95,"privacyTitle":96,"privacyLabel":97,"privacyLink":98,"certificatesGrid":99},[68,71,74,77,82,85,88],{"id":69,"title":42,"link":70},"144185264",{"__typename":44,"slug":45},{"id":72,"title":48,"link":73},"144185265",{"__typename":50},{"id":75,"title":53,"link":76},"144185266",{"__typename":55},{"id":78,"title":79,"link":80},"144185267","Blog",{"__typename":81},"BlogPostOverviewRecord",{"id":83,"title":58,"link":84},"144185268",{"__typename":44,"slug":60},{"id":86,"title":36,"link":87},"144185269",{"__typename":38},{"id":89,"title":90,"link":91},"144185270","FAQ",{"__typename":44,"slug":92},"faq","Creative Commons licence and disclaimer","CC BY 4.0","https:\u002F\u002Fcreativecommons.org\u002Flicenses\u002Fby\u002F4.0\u002F","De Voorhoede privacy statement (pdf)","Privacy statement","https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1763455455-vh-isms-006-privacy-statement-de-voorhoede-en.pdf",[100,112,123],{"id":101,"image":102,"link":107},"Xq4bBfg_TZ6Fkjax9mkbLQ",{"url":103,"alt":104,"width":105,"height":106},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1687353463-b-corp-logo-black-rgb.png","B Corp logo",404,680,{"__typename":108,"id":109,"title":110,"url":111},"ExternalLinkRecord","fGW1ak8XQYaYDLkBSyncog","B Corp","https:\u002F\u002Fwww.bcorporation.net\u002Fen-us\u002Ffind-a-b-corp\u002Fcompany\u002Fde-voorhoede\u002F",{"id":113,"image":114,"link":119},"c5mCXRTiSraRIB25fw1p7Q",{"url":115,"alt":116,"width":117,"height":118},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1687353461-dda-boxlogo-black.png","Dutch Digital Agencies logo",627,480,{"__typename":108,"id":120,"title":121,"url":122},"P6Jh7B0cTv2cKyNEeKVWVQ","Dutch Digital Agencies","https:\u002F\u002Fdutchdigitalagencies.com\u002Fleden\u002Fde-voorhoede\u002F",{"id":124,"image":125,"link":129},"MT5SCyNxSTSr_v5eeATMZw",{"url":126,"alt":127,"width":128,"height":128},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1775730283-dnv.png","DNV logo",518,{"id":130,"title":131,"link":132},"BRtNB5HnT5i-7HkA8IYzBw","DIV",{"__typename":44,"slug":133},"impact\u002Fdigitale-producten-privacy-by-design",[135],{"locale":136,"value":137},"en","cms-driven-intellisense-in-your-code-editor",{"page":139},{"slug":137,"i18nSlugs":140,"social":142,"title":143,"subtitle":79,"isArchived":146,"headerIllustration":145,"date":147,"authors":148,"introTitle":157,"items":158,"pivots":227,"relatedBlogPosts":228,"tags":229,"onMountedScript":151,"onUnmountedScript":151},[141],{"locale":136,"value":137},{"title":143,"description":144,"image":145},"CMS-driven IntelliSense in your code editor","Headless CMS’es separate content management from your front-end. This disconnect means your code editor doesn’t know about your data structure. How to fix this?",null,false,"2021-10-27T02:00:00.000+02:00",[149],{"name":150,"lastName":151,"slug":152,"image":153},"Jasper","","jasper",{"url":154,"alt":145,"width":155,"height":156},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1683535518-jasper.jpg",1892,2523,"Headless CMS’es with auto-generated API’s are popular. They separate content management from your front-end, giving you extra freedom. But this disconnect also means your code editor doesn’t know about your data structure and can’t help you out. How can we fix this?",[159,163,167,172,175,179,182,185,189,192,195,199,208,211,215,218,221,224],{"__typename":160,"id":161,"title":151,"body":162},"TextSectionRecord","68408622","\u003Cp>\u003Cspan>We&rsquo;ve worked with different \u003Ca href=\"https:\u002F\u002Fwww.voorhoede.nl\u002Fen\u002Fservices\u002Fheadless-cms\u002F\">headless CMS&rsquo;es\u003C\u002Fa> over the years, with Contentful and DatoCMS being our favourites. These modern CMS-as-a-service typically generate a complete GraphQL API for all your data models automatically. \u003C\u002Fspan>\u003Ca href=\"https:\u002F\u002Fgraphql.org\u002Flearn\u002F\">GraphQL is a strongly typed language\u003C\u002Fa>\u003Cspan>. So if we can bring these types to our code editor, we&rsquo;ll be able to get IntelliSense - like autocomplete and instant validation - for data from our CMS. Let&rsquo;s explore how to achieve this for local GraphQL, JavaScript and TypeScript files.\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":160,"id":164,"title":165,"body":166},"68408623","Introspecting our CMS API","\u003Cp>\u003Cspan>We can ask our CMS what types, queries and mutations it supports using \u003C\u002Fspan>\u003Ca href=\"https:\u002F\u002Fgraphql.org\u002Flearn\u002Fintrospection\u002F\">GraphQL&rsquo;s introspection\u003C\u002Fa>\u003Cspan>. With introspection we call the same GraphQL endpoint we use to query data from our CMS. But instead of querying data, we fetch the schema which describes the types of our data models. An introspection query typically looks something like this:\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":168,"id":169,"language":170,"body":171},"CodeBlockRecord","68408624","graphql","query IntrospectionQuery {\n  __schema {\n    types { ...FullType }\n    queryType { name }\n    mutationType { name }\n    subscriptionType { name }\n    directives {\n      name, description, locations\n      args { ...InputValue }\n    }\n  }\n}\n\nfragment FullType on __Type { ... }\nfragment TypeRef on __Type { ... }\nfragment InputValue on __InputValue { ... }",{"__typename":160,"id":173,"title":151,"body":174},"68408625","\u003Cp>\u003Cspan>It returns a very lengthy response containing the entire schema with all types. The one from this website is about 50K lines of code. It contains types like a BlogPostRecord which in turn links to a FileField for its image and a PersonRecord for its authors field:\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":168,"id":176,"language":177,"body":178},"68408626","js","{\n  \"__schema\": {\n    \"types\": [{\n        \"kind\": \"OBJECT\",\n        \"name\": \"BlogPostRecord\",\n        \"description\": \"Record of type Blog Post\",\n        \"fields\": [{\n            \"name\": \"title\",\n            \"type\": { \"kind\": \"SCALAR\", \"name\": \"String\" },\n            ...\n          },{\n            \"name\": \"image\",\n            \"type\": { \"kind\": \"OBJECT\", \"name\": \"FileField\" },\n            ...\n          },{\n            \"name\": \"authors\",\n            \"type\": {\n              \"kind\": \"NON_NULL\"\n              \"ofType\": {\n                \"kind\": \"LIST\"\n                \"ofType\": {\n                  \"kind\": \"NON_NULL\"\n                  \"ofType\": { \"kind\": \"OBJECT\", \"name\": \"PersonRecord\" },\n            ...",{"__typename":160,"id":180,"title":151,"body":181},"68408627","\u003Cp>This schema is what enables us to validate our code and add autocomplete to our editors.\u003C\u002Fp>\n\u003Cp>We can use a tool like \u003Ca href=\"https:\u002F\u002Fwww.graphql-code-generator.com\u002Fdocs\u002Fgetting-started\u002Finstallation#setup\">GraphQL Code Generator\u003C\u002Fa> to automatically fetch and save the schema, by pointing to our CMS and enabling introspection in a standardised \u003Ca href=\"https:\u002F\u002Fgraphql-config.com\u002Fintroduction\">GraphQL Config\u003C\u002Fa>:\u003C\u002Fp>",{"__typename":168,"id":183,"language":177,"body":184},"68408628","\u002F* .\u002F.graphqlrc.js *\u002F\nrequire('dotenv-safe').config()\nconst { DATO_API_TOKEN } = process.env\n\nmodule.exports = {\n  schema: {\n    'https:\u002F\u002Fgraphql.datocms.com': {\n      headers: { Authorization: DATO_API_TOKEN },\n    },\n  },\n  extensions: {\n    codegen: {\n      generates: {\n        'src\u002Flib\u002Fdato.schema.json': {\n          plugins: ['introspection'],\n        }\n} } } }",{"__typename":160,"id":186,"title":187,"body":188},"68408629","Using a schema to validate GraphQL files","\u003Cp>In our \u003Ca href=\"https:\u002F\u002Fwww.voorhoede.nl\u002Fen\u002Fblog\u002Fcomponentize-data-with-graphql-fragments\u002F\">blog post\u003C\u002Fa> \u003Ca href=\"https:\u002F\u002Fwww.voorhoede.nl\u002Fen\u002Fblog\u002Fcomponentize-data-with-graphql-fragments\u002F\">Componentize data with GraphQL Fragments\u003C\u002Fa> we described how you can structure your GraphQL queries and fragments in \u003Ccode>.graphql\u003C\u002Fcode> files. The good news is that we can use our GraphQL schema from above to validate these GraphQL files.\u003C\u002Fp>\n\u003Cp>We can \u003Ca href=\"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fwebpack-graphql-loader#specifying-options\">enable validation by configuring the GraphQL loader\u003C\u002Fa> from our previous blog post to use the schema:\u003C\u002Fp>",{"__typename":168,"id":190,"language":177,"body":191},"68408630","webpackConfig.module.rules.push({\n  test: \u002F\\.graphql?$\u002F,\n  use: [{\n    loader: 'webpack-graphql-loader',\n    options: {\n      validate: true,\n      schema: '.\u002Flib\u002Fdato.schema.json'\n    }\n  }]\n})",{"__typename":160,"id":193,"title":151,"body":194},"68408631","\u003Cp>\u003Cspan>Note that other more elaborate GraphQL tools like Apollo may do this for you under the hood. But if you want a lightweight setup without the overhead of libraries, this gives you validation for free.\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":160,"id":196,"title":197,"body":198},"68408632","Using a schema for autocomplete in code editors","\u003Cp>\u003Cspan>The setup above gives us validation during run and build-time. But depending on your editor we can do even better and enable direct validation and autocomplete while we&rsquo;re writing our GraphQL code. For example the \u003C\u002Fspan>\u003Ca href=\"https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=GraphQL.vscode-graphql\">VSCode GraphQL extension\u003C\u002Fa>\u003Cspan>automatically picks up on our \u003C\u002Fspan>\u003Ccode>.graphqlrc.yaml\u003C\u002Fcode>\u003Cspan>, understands the custom imports, and brings IntelliSense directly to our \u003C\u002Fspan>\u003Ccode>.graphql\u003C\u002Fcode>\u003Cspan> files:\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":200,"id":201,"image":202,"caption":151,"fullWidth":146,"captionPosition":207},"ImageRecord","68408633",{"url":203,"alt":204,"width":205,"height":206},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1635335597-graphql-intellisense.gif","Moving image where code is being edited",884,416,"bottom",{"__typename":160,"id":209,"title":151,"body":210},"68408634","\u003Cp>Note: restart VSCode after installing the extension for the IntelliSense to kick in.\u003C\u002Fp>",{"__typename":160,"id":212,"title":213,"body":214},"68408635","Turning a GraphQL schema into TypeScript typings","\u003Cp>\u003Cspan>Finally we can bring our content types into our TypeScript and JavaScript files by converting GraphQL types to TypeScript typings and importing them in our code. To generate the TypeScript typings we extend our GraphQL config file using the \u003C\u002Fspan>\u003Ccode>@graphql-codegen\u002Ftypescript\u003C\u002Fcode>\u003Cspan> plugin:\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":168,"id":216,"language":177,"body":217},"68408636","    codegen: {\n      generates: {\n        'src\u002Flib\u002Fdato.schema.json': {\n          plugins: ['introspection'],\n        },\n        'src\u002Flib\u002Fdato.d.ts': {\n          plugins: ['typescript'],\n        }",{"__typename":160,"id":219,"title":151,"body":220},"68408637","\u003Cp>\u003Cspan>Naturally you can use the \u003C\u002Fspan>\u003Ccode>dato.d.ts\u003C\u002Fcode>\u003Cspan> typings directly in your TypeScript files. But you can also use them in your JavaScript files to document your code and benefit from IntelliSense. The trick to achieve this is using \u003C\u002Fspan>\u003Ca href=\"https:\u002F\u002Fwww.typescriptlang.org\u002Fdocs\u002Fhandbook\u002Fjsdoc-supported-types.html#import-types\">TypeScript infused JSDoc syntax\u003C\u002Fa>\u003Cspan>:\u003C\u002Fspan>\u003C\u002Fp>",{"__typename":168,"id":222,"language":177,"body":223},"68408638","\u002F**\n * @param {object} props\n * @param {string} props.locale\n * @param {import('..\u002Flib\u002Fdato').PageRecord} props.page\n *\u002F\nexport default function Blog({ locale, page }) {\n \u002F\u002F ...",{"__typename":160,"id":225,"title":151,"body":226},"68408639","\u003Cp>Enjoy the enhanced GraphQL authoring experience.\u003C\u002Fp>",[],[],[230],{"id":231,"title":232,"slug":233,"blogPosts":234},"WTPJX79URE-O5lpWLJeEHg","Headless CMS","headless-cms",[235,242,259],{"slug":236,"title":237,"date":238,"authors":239},"dropbox-paper-as-a-headless-cms","Dropbox Paper as a headless CMS","2019-01-25T01:00:00.000+01:00",[240],{"name":150,"image":241},{"url":154,"alt":145,"width":155,"height":156},{"slug":243,"title":244,"date":245,"authors":246},"figma-as-a-cms-where-design-and-development-collide","Figma as a CMS; where design and development collide","2022-02-10T01:00:00.000+01:00",[247,253],{"name":248,"image":249},"Bas ",{"url":250,"alt":145,"width":251,"height":252},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1683535728-bas-g.jpg",2394,3192,{"name":254,"image":255},"Friso",{"url":256,"alt":145,"width":257,"height":258},"https:\u002F\u002Fwww.datocms-assets.com\u002F6524\u002F1683534636-placeholder.jpg",1235,1646,{"slug":260,"title":261,"date":262,"authors":263},"enriching-rich-text-with-inline-components-datocms-react","Enriching rich text with inline components (DatoCMS + React)","2021-11-19T01:00:00.000+01:00",[264],{"name":150,"image":265},{"url":154,"alt":145,"width":155,"height":156},1776256143966]