(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[4066],{8737:function(s,e,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/docs/guide/circular-references",function(){return n(7913)}])},7913:function(s,e,n){"use strict";n.r(e),n.d(e,{__N_SSG:function(){return r},frontmatter:function(){return c}});var a=n(5250),l=n(7160),i=n(3210),r=!0;let c={title:"Circular References",menu:"Guide",description:"Guide for how circular references and dependencies are managed in Pothos"},t=i.k;function h(s){let e=Object.assign({h1:"h1",p:"p",h2:"h2",code:"code",pre:"pre",span:"span",ul:"ul",li:"li",a:"a"},(0,l.ah)(),s.components);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(e.h1,{id:"circular-references",children:"Circular References"}),"\n",(0,a.jsx)(e.p,{children:"Circular references and Circular dependencies are common problem that can appear in a number of\nways, and cause a variety of different issues."}),"\n",(0,a.jsx)(e.p,{children:"Pothos has a number of built in mitigations to help avoid these issues, and tries to provide\nadditional APIs to help with situations that can't be automatically avoided."}),"\n",(0,a.jsx)(e.p,{children:"This guide should provide some insight into how to resolve any issues you may run into, but will\nhopefully not be needed very often."}),"\n",(0,a.jsx)(e.h2,{id:"imports",children:"imports"}),"\n",(0,a.jsx)(e.p,{children:"Circular imports are something that can cause issues in any javascript or typescript project, but\ncan become more common in graphql because of its interconnected nature."}),"\n",(0,a.jsx)(e.p,{children:"When js/ts files either directly or indirectly import each other, the exports from one file will\ninitially be undefined while executing the main body of the other. These issues often result in\nconfusing and unrelated errors because the relevant values are often not used until much later."}),"\n",(0,a.jsxs)(e.p,{children:["Pothos mitigates this by deferring a lot of the processing until the ",(0,a.jsx)(e.code,{children:"builder.toSchema()"})," method is\ncalled. As long as the file that builds the schema (calls the ",(0,a.jsx)(e.code,{children:"toSchema"})," method) is not imported by\nany other file that defines parts of the schema, this will ensure that all types are properly\nimported, and types are not unexpectedly undefined."]}),"\n",(0,a.jsxs)(e.p,{children:["As you can see in the example below, the references to ",(0,a.jsx)(e.code,{children:"Post"})," and ",(0,a.jsx)(e.code,{children:"User"})," when defining fields are\nwrapped inside a the ",(0,a.jsx)(e.code,{children:"fields"})," function. Because this function is not executed until the schema is\nloaded, these types of Circular imports should work without causing any issues."]}),"\n",(0,a.jsx)(e.pre,{children:(0,a.jsxs)(e.code,{className:"hljs language-ts",children:[(0,a.jsx)(e.span,{className:"hljs-comment",children:"// user.ts"}),"\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"import"})," { ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"Post"})," } ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"from"})," ",(0,a.jsx)(e.span,{className:"hljs-string",children:"'./post'"}),"\n\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"const"})," ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"})," = builder.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"objectType"}),"(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'User'"}),", {\n    ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"fields"}),": ",(0,a.jsxs)(e.span,{className:"hljs-function",children:[(0,a.jsx)(e.span,{className:"hljs-params",children:"t"})," =>"]})," ({ ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"posts"}),": t.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"expose"}),"(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'posts'"}),", { ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"type"}),": [",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"Post"}),"]}) }),\n})\n\n",(0,a.jsx)(e.span,{className:"hljs-comment",children:"// post.ts"}),"\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"import"})," { ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"})," } ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"from"})," ",(0,a.jsx)(e.span,{className:"hljs-string",children:"'./user'"}),"\n\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"const"})," ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"Post"})," = builder.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"objectType"}),"(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'Post'"}),", {\n    ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"fields"}),": ",(0,a.jsxs)(e.span,{className:"hljs-function",children:[(0,a.jsx)(e.span,{className:"hljs-params",children:"t"})," =>"]})," ({ ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"author"}),": t.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"expose"}),"(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'author'"}),", {{ ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"type"}),": ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"})," }}) }),\n})\n\n",(0,a.jsx)(e.span,{className:"hljs-comment",children:"// schema.js"}),"\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"const"})," schema = builder.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"toSchema"}),"()\n"]})}),"\n",(0,a.jsxs)(e.p,{children:["Another best practice is to avoid importing from ",(0,a.jsx)(e.code,{children:"index.ts"})," files by importing from the file that\ndefines the value directly. The easiest way to achieve this is by not exporting values from\n",(0,a.jsx)(e.code,{children:"index.ts"})," files."]}),"\n",(0,a.jsx)(e.pre,{children:(0,a.jsxs)(e.code,{className:"hljs language-ts",children:[(0,a.jsx)(e.span,{className:"hljs-comment",children:"// bad"}),"\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," * ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"from"})," ",(0,a.jsx)(e.span,{className:"hljs-string",children:"'./enums'"}),";\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," * ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"from"})," ",(0,a.jsx)(e.span,{className:"hljs-string",children:"'./objects'"}),";\n",(0,a.jsx)(e.span,{className:"hljs-comment",children:"// better"}),"\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"import"})," ",(0,a.jsx)(e.span,{className:"hljs-string",children:"'./enums'"}),";\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"import"})," ",(0,a.jsx)(e.span,{className:"hljs-string",children:"'./objects'"}),";\n"]})}),"\n",(0,a.jsx)(e.h2,{id:"defining-circular-or-recursive-types",children:"Defining Circular or Recursive types"}),"\n",(0,a.jsx)(e.p,{children:"A large portion of the Pothos API is designed to work well with circular references, but there are a\nfew cases where typescript is unable to resolve circular references correctly."}),"\n",(0,a.jsx)(e.p,{children:"What should work without any issues:"}),"\n",(0,a.jsxs)(e.ul,{children:["\n",(0,a.jsx)(e.li,{children:"Objects and interfaces referenced via a class"}),"\n",(0,a.jsx)(e.li,{children:"Objects and interfaces referenced via a string (by proving a type mapping when creating the\nSchemaBuilder)"}),"\n",(0,a.jsx)(e.li,{children:"Objects defined by plugins like Prisma that derive type information from an external source"}),"\n"]}),"\n",(0,a.jsx)(e.p,{children:"Cases that may require some modification"}),"\n",(0,a.jsxs)(e.ul,{children:["\n",(0,a.jsx)(e.li,{children:"Input objects with circular references"}),"\n",(0,a.jsxs)(e.li,{children:["Object types defined with ",(0,a.jsx)(e.code,{children:"builder.objectRef"})]}),"\n",(0,a.jsxs)(e.li,{children:["Objects defined by plugins like ",(0,a.jsx)(e.code,{children:"dataloader"})," that infer the Backing mode type from options passed\nto the type."]}),"\n"]}),"\n",(0,a.jsx)(e.h2,{id:"input-objects",children:"Input objects"}),"\n",(0,a.jsxs)(e.p,{children:["Defining recursive input types is described in the ",(0,a.jsx)(e.a,{href:"./inputs#recursive-inputs",children:"input Guide"})]}),"\n",(0,a.jsx)(e.h2,{id:"object-refs",children:"Object refs"}),"\n",(0,a.jsxs)(e.p,{children:["Object refs may cause issues with circular references if the refs are implemented before they are\nassigned to a variable. This can easily be avoided by moving the call to ",(0,a.jsx)(e.code,{children:"ref.implement"})," into its\nown statement."]}),"\n",(0,a.jsx)(e.pre,{children:(0,a.jsxs)(e.code,{className:"hljs language-typescript",children:[(0,a.jsx)(e.span,{className:"hljs-comment",children:"// May cause issues"}),"\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"const"})," ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"})," = builder.",(0,a.jsx)(e.span,{className:"hljs-property",children:"objectRef"}),"<",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"IUser"}),">(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'User'"}),").",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"implement"}),"({...});\n\n",(0,a.jsx)(e.span,{className:"hljs-comment",children:"// Should be safe"}),"\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"const"})," ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"})," = builder.",(0,a.jsx)(e.span,{className:"hljs-property",children:"objectRef"}),"<",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"IUser"}),">(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'User'"}),")\n\n",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"}),".",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"implement"}),"({...});\n"]})}),"\n",(0,a.jsx)(e.p,{children:"Using object refs is often a great way to avoid issues with circular references because it allows\nyou to define the reference before defining any fields for your type. Many of the builder methods in\nPothos and its plugins can be passed a type ref instead of a name:"}),"\n",(0,a.jsx)(e.pre,{children:(0,a.jsxs)(e.code,{className:"hljs language-typescript",children:[(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"const"})," ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"})," = builder.",(0,a.jsx)(e.span,{className:"hljs-property",children:"objectRef"}),"<",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"IUser"}),">(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'User'"}),");\n\nbuilder.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"objectType"}),"(",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"}),", {\n  ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"field"}),": ",(0,a.jsxs)(e.span,{className:"hljs-function",children:["(",(0,a.jsx)(e.span,{className:"hljs-params",children:"t"}),") =>"]})," ({\n    ",(0,a.jsx)(e.span,{className:"hljs-comment",children:"// Circular references here won't cause issues, because User is already defined above"}),"\n  }),\n});\n"]})}),"\n",(0,a.jsx)(e.h2,{id:"defining-fields-separately",children:"Defining fields separately"}),"\n",(0,a.jsx)(e.p,{children:"Another easy work around is to define any fields that are causing issues separately"}),"\n",(0,a.jsx)(e.pre,{children:(0,a.jsxs)(e.code,{className:"hljs language-ts",children:[(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"const"})," ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"})," = builder.",(0,a.jsx)(e.span,{className:"hljs-property",children:"objectRef"}),"<",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"UserType"}),">(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'User'"}),").",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"implement"}),"({\n  ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"fields"}),": ",(0,a.jsxs)(e.span,{className:"hljs-function",children:["(",(0,a.jsx)(e.span,{className:"hljs-params",children:"t"}),") =>"]})," ({ ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"posts"}),": t.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"expose"}),"(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'posts'"}),", { ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"type"}),": [",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"Post"}),"] }) }),\n});\n\n",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"export"})," ",(0,a.jsx)(e.span,{className:"hljs-keyword",children:"const"})," ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"Post"})," = builder.",(0,a.jsx)(e.span,{className:"hljs-property",children:"objectRef"}),"<",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"PostType"}),">(",(0,a.jsx)(e.span,{className:"hljs-string",children:"'Post'"}),").",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"implement"}),"({\n  ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"fields"}),": ",(0,a.jsxs)(e.span,{className:"hljs-function",children:["(",(0,a.jsx)(e.span,{className:"hljs-params",children:"t"}),") =>"]})," ({\n    ",(0,a.jsx)(e.span,{className:"hljs-comment",children:"// No more circular reference"}),"\n  }),\n});\n\nbuilder.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"objectField"}),"(",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"Post"}),", ",(0,a.jsx)(e.span,{className:"hljs-string",children:"'author'"}),", ",(0,a.jsxs)(e.span,{className:"hljs-function",children:["(",(0,a.jsx)(e.span,{className:"hljs-params",children:"t"}),") =>"]})," t.",(0,a.jsx)(e.span,{className:"hljs-title function_",children:"expose"}),"({ ",(0,a.jsx)(e.span,{className:"hljs-attr",children:"type"}),": ",(0,a.jsx)(e.span,{className:"hljs-title class_",children:"User"})," }));\n"]})})]})}e.default=function(){let s=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return(0,a.jsx)(t,Object.assign({},s,{children:(0,a.jsx)(h,s)}))}}},function(s){s.O(0,[8430,3210,9774,2888,179],function(){return s(s.s=8737)}),_N_E=s.O()}]);