PageSourceSearch

https://app.takeshape.io/docs/assets/js/fa9375d6.804055fc.js

js takeshape.io collected 2026-10-03 19:17:30 UTC 68,420 bytes, 1 lines download raw bytes

1(self.webpackChunk_takeshape_docs=self.webpackChunk_takeshape_docs||[]).push([[1994],{7093:(e,t,s)=>{"use strict";s.r(t),s.d(t,{assets:()=>l,contentTitle:()=>c,default:()=>m,frontMatter:()=>h,metadata:()=>n,toc:()=>p});const n=JSON.parse('{"id":"schema/shapes","title":"Shapes","description":"Shapes are objects in your project schema that represent data your schema will fetch, mutate and store in your project.","source":"@site/docs/schema/shapes.mdx","sourceDirName":"schema","slug":"/schema/shapes","permalink":"/docs/schema/shapes","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":1,"frontMatter":{"sidebar_position":1,"sidebar_label":"Shapes"},"sidebar":"tutorialSidebar","previous":{"title":"Introduction","permalink":"/docs/schema/intro"},"next":{"title":"Queries & Mutations","permalink":"/docs/schema/queries-mutations"}}');var i=s(65723),a=s(16585),r=s(88725),o=s(35089),d=s(75778);const h={sidebar_position:1,sidebar_label:"Shapes"},c="Shapes",l={},p=[{value:"Creating Shapes",id:"creating-shapes",level:2},{value:"Shape Editor",id:"shape-editor",level:3},{value:"Metadata",id:"metadata",level:4},{value:"Properties",id:"properties",level:4},{value:"Storage",id:"storage",level:4},{value:"Edit and query shape data",id:"edit-and-query-shape-data",level:3},{value:"Schema JSON",id:"schema-json",level:3},{value:"Defining a basic Shape",id:"defining-a-basic-shape",level:4},{value:"Configuring a Shape schema",id:"configuring-a-shape-schema",level:4},{value:"Service Schema Shapes",id:"service-schema-shapes",level:2},{value:"Referencing Shapes",id:"referencing-shapes",level:3},{value:"Extending data",id:"extending-data",level:3},{value:"Extending API data",id:"extending-api-data",level:3},{value:"Extending to modify default behaviors",id:"extending-to-modify-default-behaviors",level:3},{value:"Examples",id:"examples",level:2},{value:"Basic Shape example",id:"basic-shape-example",level:3},{value:"Complex Shape example",id:"complex-shape-example",level:3},{value:"Generated Shape example",id:"generated-shape-example",level:3},...o.RM];function u(e){const t={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.header,{children:(0,i.jsx)(t.h1,{id:"shapes",children:"Shapes"})}),"\n",(0,i.jsx)(t.p,{children:"Shapes are objects in your project schema that represent data your schema will fetch, mutate and store in your project."}),"\n",(0,i.jsxs)(t.p,{children:["At a low level, a shape is a JSON object that lists what properties certain data will have\u2014it specifies the ",(0,i.jsx)(t.em,{children:"shape"})," of the data. This list of properties is used to generate the GraphQL API and a admin UI experience for data editing. You can think of it like a type in a GraphQL API."]}),"\n",(0,i.jsxs)(t.p,{children:["For example, a ",(0,i.jsx)(t.code,{children:"Product"})," shape should list the properties a product would have, like ",(0,i.jsx)(t.code,{children:"id"}),", ",(0,i.jsx)(t.code,{children:"title"}),", and ",(0,i.jsx)(t.code,{children:"price"}),". Those properties should be typed appropriately, like ",(0,i.jsx)(t.code,{children:"string"})," for ",(0,i.jsx)(t.code,{children:"id"})," and ",(0,i.jsx)(t.code,{children:"title"}),", and ",(0,i.jsx)(t.code,{children:"integer"})," or ",(0,i.jsx)(t.code,{children:"number"})," for ",(0,i.jsx)(t.code,{children:"price"}),". Individual product objects will have values for those properties, and the GraphQL API exposes type information thanks to the ",(0,i.jsx)(t.code,{children:"Product"})," shape."]}),"\n",(0,i.jsxs)(t.p,{children:["At a higher level, a shape is a representation of data you want to manipulate. When you create shapes in the admin UI editor, the queries and mutations to fetch and update instances of that shape are automatically generated. By creating shapes in your project's JSON sc
1hema, you can join content from various services into a single data structure stored ",(0,i.jsx)(t.a,{href:"/data/intro",children:"in ShapeDB"}),". You can then expose that data to your frontend applications via your project's GraphQL API."]}),"\n",(0,i.jsxs)(t.p,{children:["This guide will teach you how to use shapes. For more information, check out ",(0,i.jsx)(t.a,{href:"/schema/spec#shapes",children:"the section on shapes in our schema spec reference"}),"."]}),"\n",(0,i.jsx)(t.h2,{id:"creating-shapes",children:"Creating Shapes"}),"\n",(0,i.jsx)(t.h3,{id:"shape-editor",children:"Shape Editor"}),"\n",(0,i.jsxs)(t.p,{children:["In the admin UI, navigate to the ",(0,i.jsx)(t.strong,{children:"API"})," tab. Select the ",(0,i.jsx)(t.strong,{children:"Workbench"})," tab in the ",(0,i.jsx)(t.strong,{children:"Workbench"})," panel at the center of the page. Select the ",(0,i.jsx)(t.strong,{children:"Add Shape"})," button to begin creating a shape."]}),"\n",(0,i.jsx)(r.A,{alt:"A screenshot of the buttons to press to navigate to shapes in the admin UI.",img:s(34959)}),"\n",(0,i.jsx)(t.p,{children:"As you make changes to your shape, the scratch schema will be automatically updated in the background. This way, you can immediately preview any changes in your GraphQL API."}),"\n",(0,i.jsx)(t.p,{children:'Once you\'re happy with your changes, you can save them using the purple "Deploy" button in the schema toolbar.'}),"\n",(0,i.jsx)(t.h4,{id:"metadata",children:"Metadata"}),"\n",(0,i.jsxs)(t.p,{children:["In the shape editor, you can specify the ",(0,i.jsx)(t.code,{children:"title"})," and ",(0,i.jsx)(t.code,{children:"name"})," of your shape:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["The ",(0,i.jsx)(t.code,{children:"name"})," must contain no spaces, dashes, underscores or special characters."]}),"\n",(0,i.jsxs)(t.li,{children:["The ",(0,i.jsx)(t.code,{children:"title"})," can be more human-readable, with spaces, underscores and dashes, but with no special characters."]}),"\n"]}),"\n",(0,i.jsx)(r.A,{alt:"A screenshot of the editor. The shape being edited is named NewShape and automatically titled New Shape.",img:s(8343)}),"\n",(0,i.jsx)(t.h4,{id:"properties",children:"Properties"}),"\n",(0,i.jsx)(t.p,{children:"You can add, edit, and remove properties on the shape."}),"\n",(0,i.jsx)(t.p,{children:'Add a property from the form at the bottom of the list. Pick the property\'s type and give it a name, then use the "plus" button to add it.'}),"\n",(0,i.jsxs)(t.p,{children:["Selecting a property will open its editor panel, with the ability to configure property metadata like name, title, and description, as well as configure the form widget for ",(0,i.jsx)(t.a,{href:"/data/editor",children:"editing the property in ShapeDB"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["While a property is selected, you can select adjacent properties using the up and down arrow keys. Properties can be reordered using drag-and-drop or ",(0,i.jsx)(t.code,{children:"Shift + Up/Down Arrow"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["To delete a property, use the trash can button next to it in the list; or, use the ",(0,i.jsx)(t.code,{children:"Delete"})," key when the property is selected."]}),"\n",(0,i.jsxs)(t.p,{children:["You can learn more about property editing in our guide to ",(0,i.jsx)(t.a,{href:"/data/modeling",children:"data modeling"}),"."]}),"\n",(0,i.jsx)(r.A,{alt:"A screenshot of the editor. The name property is selected and its configuration sidebar is open. It is marked as required.",img:s(39362)}),"\n",(0,i.jsx)(t.h4,{id:"storage",children:"Storage"}),"\n",(0,i.jsxs)(t.p,{children:["Shapes on their own are useful for representing data stored in external services, but sometimes you want to store data that isn't available on a given service. There is a ",(0,i.jsx)(t.a,{href:"/data/intro",children:"built-in database called ShapeDB"})," which can store data using your shape as a model."]}),"\n",(0,i.jsx)(t.p,{children:"The three storage options are:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.strong,{children:"None"}),' \u2014 Store nothing in ShapeDB. Best for defining shapes you\'ll reuse inside other shapes, like "SEO Metadata," or shapes for data that will be stored in an external service.']}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.strong,{children:"Single"})," \u2014 Store one instance of this shape in ShapeDB. This makes sense if your shape represents data there won't be multiple instances of, like a ",(0,i.jsx)(t.code,{children:"Homepage"})," or ",(0,i.jsx)(t.code,{children:"Settings"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:["A ",(0,i.jsx)(t.code,{children:"getShape"})," query, as well as ",(0,i.jsx)(t.code,{children:"updateShape"}),", ",(0,i.jsx)(t.code,{children:"deleteShape"}),", and ",(0,i.jsx)(t.code,{children:"createShape"})," mutations are added to your project schema. For example, if you create a ",(0,i.jsx)(t.code,{children:"Homepage"})," shape, your schema will have ",(0,i.jsx)(t.code,{children:"getHomepage"}),", ",(0,i.jsx)(t.code,{children:"createHomepage"}),", ",(0,i.jsx)(t.code,{children:"deleteHomepage"})," and ",(0,i.jsx)(t.code,{children:"updateHomepage"})," in it. You can then call these queries from your frontend applications or test them in the API Explorer."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.strong,{children:"Multiple"})," \u2014 Store multiple instances of this shape. Best for data that you expect multiple instances of, like ",(0,i.jsx)(t.code,{children:"Profile"})," or ",(0,i.jsx)(t.code,{children:"Product"})," shapes."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:["In addition to the same queries and mutations added to your schema for ",(0,i.jsx)(t.strong,{children:"Single"})," storage shapes, a ",(0,i.jsx)(t.code,{children:"getShapeList"})," query will be added. For example, a ",(0,i.jsx)(t.code,{children:"Product"})," shape with ",(0,i.jsx)(t.strong,{children:"Multiple"})," storage will add a ",(0,i.jsx)(t.code,{children:"getProductList"})," query to your schema."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:["You can also switch on the ",(0,i.jsx)(t.strong,{children:"Taxonomy"})," option to use this shape to categorize other shapes. For example, a ",(0,i.jsx)(t.code,{children:"Product Categories"})," shape could be used to filter a list of ",(0,i.jsx)(t.code,{children:"Product"}),"s. Learn more in ",(0,i.jsx)(t.a,{href:"/data/modeling#taxonomy",children:"the Taxonomy section of our data modeling docs"}),"."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["Once your shape is configured, select the ",(0,i.jsx)(t.strong,{children:"Deploy"})," button at the top of the page, and your changes will be saved to your project schema."]}),"\n",(0,i.jsx)(r.A,{alt:"A screenshot of the editor's shape storage controls. The Multiple storage configuration is selected.",img:s(90127)}),"\n",(0,i.jsx)(t.h3,{id:"edit-and-query-shape-data",children:"Edit and query shape data"}),"\n",(0,i.jsxs)(t.p,{children:["Navigate to the ",(0,i.jsx)(t.strong,{children:"Data"})," tab to see your new shape, then select it in the list on the left. Select the ",(0,i.jsx)(t.strong,{children:"New Shape"})," button at the top-right of the page to create an instance of that shape. For example, if your product's title is \"My Custom Product,\" the button's text will be ",(0,i.jsx)(t.strong,{children:"New My Custom Product"}),"."]}),"\n",(0,i.jsx)(r.A,{alt:"A screenshot of My Custom Product in the Data tab.",img:s(98853)}),"\n",(0,i.jsxs)(t.p,{children:["You can then fill out the fields you specified in the shape editor to create an instance of the shape. Be sure to select ",(0,i.jsx)(t.strong,{children:"Enabled"})," at the right side of the page before saving your shape. Otherwise it won't be accessible from your API."]}),"\n",(0,i.jsxs)(t.p,{children:["Now navigate to the ",(0,i.jsx)(t.strong,{children:"API"})," tab and select the ",(0,i.jsx)(t.strong,{children:"Workbench"})," tab. You can scroll through your list of shapes and queries here, or you can search for it in the search bar above the list. Select the ",(0,i.jsx)(t.strong,{children:"Preview in API Explorer"})," button to the right of your shape, and a query to fetch your data will be generated in the ",(0,i.jsx)(t.strong,{children:"API Explorer"}),". Select the ",(0,i.jsx)(t.strong,{children:"Run"})," button above the ",(0,i.jsx)(t.strong,{children:"API Explorer"}),", and your data will be fetched."]}),"\n",(0,i.jsxs)(t.p,{children:["You can ",(0,i.jsx)(t.a,{href:"/get-started/sending-queries",children:"run this same query in your frontend applications"}),"."]}),"\n",(0,i.jsx)(t.p,{children:"To learn how to do more complex actions with your shapes, like attach data from remote APIs to them, read the next section of this guide."}),"\n",(0,i.jsx)(t.h3,{id:"schema-json",children:"Schema JSON"}),"\n",(0,i.jsxs)(t.p,{children:["You can create shapes by editing your project's JSON sc
1hema. View your schema by navigating to the ",(0,i.jsx)(t.strong,{children:"API"})," tab, then selecting the ",(0,i.jsx)(t.strong,{children:"JSON"})," tab in the ",(0,i.jsx)(t.strong,{children:"Workbench"})," panel."]}),"\n",(0,i.jsx)(t.p,{children:"Editing the schema allows you to configure shapes that interact with remote API services. For example:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["You want to create a ",(0,i.jsx)(t.code,{children:"Product"})," shape that combines data from ",(0,i.jsx)(t.a,{href:"/services/providers/shopify",children:"Shopify"})," and Recharge. That shape would allow you to fetch products with one-time-purchase information from Shopify and subscription details from Recharge in a single query from your frontend web app."]}),"\n",(0,i.jsxs)(t.li,{children:["You want to create a ",(0,i.jsx)(t.code,{children:"Profile"})," shape that stores ",(0,i.jsx)(t.a,{href:"/services/providers/auth0",children:"Auth0"})," credentials when the ",(0,i.jsx)(t.code,{children:"createProfile"})," mutation is called. This would allow you to serve user-scoped profile data to your application without having to build out your own user authentication architecture."]}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"Configuring shapes for advanced use-cases like these unlocks the ability to be a backend for your composable e-commerce applications."}),"\n",(0,i.jsx)(t.p,{children:"First, learn how to define a shape in your schema in the section below."}),"\n",(0,i.jsx)(t.admonition,{title:"Check out our starters",type:"tip",children:(0,i.jsxs)(t.p,{children:["You can see an example of a ",(0,i.jsx)(t.code,{children:"Profile"})," shape that uses Auth0 in our E-commerce Kitchen Sink starter. ",(0,i.jsx)(d.A,{path:"/add-pattern?repo=https://github.com/takeshape/takeshape-starter-ecommerce-kitchen-sink/tree/main/.takeshape/pattern",children:"Deploy the pattern"})," to test it out. ",(0,i.jsx)(t.a,{href:"https://github.com/takeshape/takeshape-starter-ecommerce-kitchen-sink",children:"Clone the repo"})," to run the project on your local machine."]})}),"\n",(0,i.jsx)(t.h4,{id:"defining-a-basic-shape",children:"Defining a basic Shape"}),"\n",(0,i.jsx)(t.p,{children:"All shapes must have the same required properties:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"id"'})," \u2014 A unique identifier. This can be any string. When you create a shape in the shape editor, a randomized string is generated for this value. When you manually create a shape in your schema, you can set this value to whatever unique string you'd like."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"name"'})," \u2014 A unique name string that can't contain spaces, dashes, underscores or special characters. The ",(0,i.jsx)(t.code,{children:'"name"'})," property must have the same value as the shape's object key."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"title"'})," \u2014 A unique title that can be a more human-readable than the ",(0,i.jsx)(t.code,{children:'"name"'}),". You can use spaces, underscores and dashes, but not special characters."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"schema"'})," \u2014 The object where the shape's properties are specified. Learn more below."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"Optional properties include:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"workflow"'})," \u2014 Takes the name of the workflow this shape should be scoped to as a string. Learn more in ",(0,i.jsx)(t.a,{href:"/data/workflows",children:"our workflow guide"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"model"'})," \u2014 The data model used to store instances of this shape. Valid properties include ",(0,i.jsx)(t.code,{children:'"multiple"'}),", ",(0,i.jsx)(t.code,{children:'"single"'}),", and ",(0,i.jsx)(t.code,{children:'"taxonomy"'}),". Learn more in ",(0,i.jsx)(t.a,{href:"/data/modeling",children:"our data modeling guide"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"description"'})," \u2014 A description of the shape. This description appears in the ",(0,i.jsx)(t.strong,{children:"Docs Panel"})," and can be any string."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(t.h4,{id:"configuring-a-shape-schema",children:"Configuring a Shape schema"}),"\n",(0,i.jsx)(t.p,{children:"A shape's schema is its most important property. The schema is where you define which fields exist on a shape, as well as how to fetch the data for those fields."}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:'"schema"'})," object itself has two required properties:"]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"type"'})," \u2014 The data type of the ",(0,i.jsx)(t.code,{children:'"schema"'})," property. This should almost always be ",(0,i.jsx)(t.code,{children:'"object"'}),"."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"properties"'})," \u2014 The fields the shape will have."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"There's also one optional property:"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:[(0,i.jsx)(t.code,{children:'"required"'})," \u2014 An optional array of names of properties that ",(0,i.jsx)(t.em,{children:"must"})," be filled when a new instance of the shape is created. For example, ",(0,i.jsx)(t.code,{children:'"required": ["id", "title"]'})]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:["The most important property of the ",(0,i.jsx)(t.code,{children:'"schema"'})," is the ",(0,i.jsx)(t.code,{children:'"properties"'})," object, where you can specify all fields a shape has."]}),"\n",(0,i.jsxs)(t.p,{children:["For example, a ",(0,i.jsx)(t.code,{children:"Product"})," shape might have two fields: ",(0,i.jsx)(t.code,{children:'"title"'})," and ",(0,i.jsx)(t.code,{children:'"price"'}),". Its schema would look like this:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Shape schema example"',children:'"schema": {\n    "type": "object",\n    "properties": {\n        "title": {"type": "string"},\n        "price": {"type": "number"}\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["Each attribute in the ",(0,i.jsx)(t.code,{children:'"properties"'})," object only requires an object key and a ",(0,i.jsx)(t.code,{children:'"type"'})," field within its object. There are also many optional properties that are useful when you want to store instances of a shape."]}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"@mapping"'}
1)," \u2014 This value is typically generated when you create a shape in the shape editor. It defines where to find data for instances of this shape. Changing it will cause you to lose data."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"minLength"'})," \u2014 The minimum character length for the property. For example, if the property is ",(0,i.jsx)(t.code,{children:'"handle"'})," and the shape is ",(0,i.jsx)(t.code,{children:"Product"}),", you may want the ",(0,i.jsx)(t.code,{children:"createProduct"})," mutation to fail if the ",(0,i.jsx)(t.code,{children:"handle"})," being assigned to the new product is an empty string."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"minimum"'})," \u2014 The minimum value possible for an ",(0,i.jsx)(t.code,{children:"interger"})," type property."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"description"'})," \u2014 A description that will appear in the ",(0,i.jsx)(t.strong,{children:"Docs Panel"})," of the API explorer in your project, as well as on the ",(0,i.jsx)(t.strong,{children:"Create Shape"})," page in the ",(0,i.jsx)(t.strong,{children:"Data"})," tab when you create an instance of the shape."]}),"\n"]}),"\n",(0,i.jsxs)(t.li,{children:["\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"title"'})," \u2014 A title for the property. For example, the product itself might be ",(0,i.jsx)(t.code,{children:'"sellingPlanId"'}),", but you might want to title it ",(0,i.jsx)(t.code,{children:'"Selling Plan ID"'})," so that it's more human-readable when you encounter it in the shape editor."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(t.p,{children:"Here's an example of a shape generated by the editor:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Example of a generated shape"',children:'"CustomProduct": {\n    "id": "7zR3azGWE",\n    "name": "CustomProduct",\n    "title": "My Custom Product",\n    "description": "A custom product",\n    "workflow": "default",\n    "model": {"type": "taxonomy"},\n    "schema": {\n        "type": "object",\n        "properties": {\n            "title": {\n                "type": "string",\n                "minLength": 1,\n                "description": "A title for the product",\n                "title": "Title",\n                "@mapping": "shapedb:CustomProduct.34LPOX222"\n            },\n            "price": {\n                "@l10n": false,\n                "minimum": 0,\n                "type": "integer",\n                "description": "",\n                "title": "Price",\n                "@mapping": "shapedb:CustomProduct.xaoc1ABSI"\n            }\n        },\n        "required": ["title", "price"]\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["For more information on schema properties, check out ",(0,i.jsx)(t.a,{href:"/schema/spec/#shapes",children:"the shapes section of our spec reference"}),"."]}),"\n",(0,i.jsx)(t.h2,{id:"service-schema-shapes",children:"Service Schema Shapes"}),"\n",(0,i.jsxs)(t.p,{children:["When you connect ",(0,i.jsx)(t.a,{href:"/services/intro",children:"one a service"}),", such as ",(0,i.jsx)(t.a,{href:"/services/providers/bigcommerce",children:"BigCommerce"})," or ",(0,i.jsx)(t.a,{href:"/services/providers/stripe",children:"Stripe"}),", or a GraphQL service, like Shopify Storefront, shapes are generated from the types introspected from the service's API. These shapes do not appear in your JSON schema, but they are saved in a hidden file called the ",(0,i.jsx)(t.strong,{children:"Service Schema"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Your ",(0,i.jsx)(t.strong,{children:"Service Schema"})," is a large file that contains all the shapes and queries that are generated to allow you to query data from your connected services. Although this file is hidden, you can reference queries, mutations and shapes defined in your service's documentation as if those things existed within your schema."]}),"\n",(0,i.jsxs)(t.p,{children:["For example, the ",(0,i.jsx)(t.code,{children:"Product"})," type ",(0,i.jsx)(t.a,{href:"https://developer.bigcommerce.com/legacy/ZG9jOjIyMDY3Mg-products#product-object--properties",children:"in BigCommerce"}
1)," becomes a ",(0,i.jsx)(t.code,{children:"BigCommerce_Product"})," shape."]}),"\n",(0,i.jsxs)(t.p,{children:["You can see these generated shapes in the ",(0,i.jsx)(t.strong,{children:"Docs Panel"})," by selecting the docs button above your ",(0,i.jsx)(t.strong,{children:"API Explorer"})," in the ",(0,i.jsx)(t.strong,{children:"API"})," tab. Search for the service you're interested in using. You'll see a list of types with the service's namespace prepended to the front of them. These types are also shapes available to your schema."]}),"\n",(0,i.jsxs)(t.p,{children:["For example, if you connect the Shopify Admin API using ",(0,i.jsx)(t.a,{href:"/services/providers/shopify",children:"our dedicated Shopify Admin provider"}),", you'll find a list of available shapes in the ",(0,i.jsx)(t.strong,{children:"Docs Panel"}),". These Shapes are Shopify's types with ",(0,i.jsx)(t.code,{children:"Shopify_"})," prepended to the front, like ",(0,i.jsx)(t.code,{children:"Shopify_Product"})," and ",(0,i.jsx)(t.code,{children:"Shopify_Collection"}),". In this case, ",(0,i.jsx)(t.code,{children:"Shopify"})," is the namespace for these types."]}),"\n",(0,i.jsxs)(t.p,{children:["Alternatively, you can reference these shapes by scoping them with the service's ",(0,i.jsx)(t.code,{children:"id"}),". You can find a service's ID in your JSON schema by navigating to the ",(0,i.jsx)(t.code,{children:'"services"'})," object at the root-level of the schema. You can change the ",(0,i.jsx)(t.code,{children:"id"})," there, or you can ",(0,i.jsx)(t.a,{href:"/get-started/connect-services",children:"edit your service in the admin UI"})," and change the ",(0,i.jsx)(t.strong,{children:"Slug"})," input field's value to whatever you'd like the ",(0,i.jsx)(t.code,{children:"id"})," to be."]}),"\n",(0,i.jsxs)(t.p,{children:["For example, if your Shopify Admin API's service ",(0,i.jsx)(t.code,{children:"id"})," is ",(0,i.jsx)(t.code,{children:"my-shopify-shop"}),", then you can scope shapes from the Admin API with ",(0,i.jsx)(t.code,{children:'"my-shopify-shop:X"'}),", where ",(0,i.jsx)(t.code,{children:"X"})," is whatever shape you'd like, like ",(0,i.jsx)(t.code,{children:"Product"})," or ",(0,i.jsx)(t.code,{children:"Customer"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["You can use these shapes as values for the ",(0,i.jsx)(t.code,{children:'"shape"'})," field when you configure queries and mutations, which you can read about in ",(0,i.jsx)(t.a,{href:"/schema/queries-mutations/#service-schema-shapes",children:"our queries and mutations guide"}),"."]}),"\n",(0,i.jsx)(t.p,{children:"You can also add custom properties to these shapes, or use these shapes as properties on your own custom shapes. Read the next section in this guide to learn more."}),"\n",(0,i.jsx)(t.h3,{id:"referencing-shapes",children:"Referencing Shapes"}),"\n",(0,i.jsxs)(t.p,{children:["You can reference a shape that already exists in your schema or is ",(0,i.jsx)(t.a,{href:"#using-shapes-from-connected-services",children:"available from a connected service"})," by using ",(0,i.jsx)(t.code,{children:'"@ref"'}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Let's say you want to create a shape that serves as a list of another shape, like an ",(0,i.jsx)(t.code,{children:"OrderList"})," shape that contains a list of ",(0,i.jsx)(t.code,{children:"Order"})," instances."]}),"\n",(0,i.jsxs)(t.p,{children:["Your ",(0,i.jsx)(t.code,{children:"OrderList"})," will have an array property called ",(0,i.jsx)(t.code,{children:"orders"}),", and you'll have to define the properties that the items within ",(0,i.jsx)(t.code,{children:"orders"})," will have. This is required for type information to be available when you fetch an ",(0,i.jsx)(t.code,{children:"OrderList"}),". In this case, since your ",(0,i.jsx)(t.code,{children:"orders"})," property's items are ",(0,i.jsx)(t.code,{children:"Order"})," instances, you have to explicitly define each property of the ",(0,i.jsx)(t.code,{children:"Order"})," shape."]}),"\n",(0,i.jsx)(t.p,{children:"For example:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="OrderList without @ref"',children:'"OrderList": {\n    "id": "OrderList",\n    "name": "OrderList",\n    "title": "OrderList",\n    "schema": {\n        "type": "object",\n        "properties": {\n          "orders": {\n            "type": "array",\n            "items": {\
1n                "type":"object",\n                "properties": {\n                    "prop1": {"type":"string"},\n                    "prop2": {"type":"number"},\n                    "prop3": {"type":"boolean"},\n                    "prop4": {"type":"integer"},\n                    "prop5": {"type":"string"},\n                    "prop6": {"type":"boolean"},\n                    ... etc\n                }\n            }\n          }\n        }\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["But you can skip having to paste all of the properties of an ",(0,i.jsx)(t.code,{children:"Order"})," shape by using ",(0,i.jsx)(t.code,{children:'"@ref"'}),". Instead, your ",(0,i.jsx)(t.code,{children:"OrderList"})," will look like this:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="OrderList with @ref"',children:'"OrderList": {\n    "id": "OrderList",\n    "name": "OrderList",\n    "title": "OrderList",\n    "schema": {\n        "type": "object",\n        "properties": {\n          "orders":{\n            "type": "array",\n            "items": {"@ref": "local:Order"}\n          }\n        }\n    }\n}\n'})}),"\n",(0,i.jsx)(t.admonition,{title:"Note",type:"info",children:(0,i.jsxs)(t.p,{children:["In the above example you must scope ",(0,i.jsx)(t.code,{children:"Order"})," with ",(0,i.jsx)(t.code,{children:"local:"})," so the ",(0,i.jsx)(t.code,{children:"Order"})," shape can be found in your JSON schema. In general, when using ",(0,i.jsx)(t.code,{children:'"@ref"'}),", you must scope local shapes this way. ",(0,i.jsx)(t.code,{children:'"local:X"'})]})}),"\n",(0,i.jsx)(t.p,{children:"This same logic can be used to attach any shape to any shape."}),"\n",(0,i.jsxs)(t.p,{children:["For example, a schema with ",(0,i.jsx)(t.code,{children:"ProductList"}),", ",(0,i.jsx)(t.code,{children:"FriendList"}),", and ",(0,i.jsx)(t.code,{children:"OrderList"})," shapes defined in it could have a ",(0,i.jsx)(t.code,{children:"Profile"})," shape that references a user's reviews, friends and orders as shown below:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Profile example"',children:'"Profile": {\n    "id": "Profile",\n    "name": "Profile",\n    "title": "Profile",\n    "schema": {\n        "type": "object",\n        "properties": {\n          "userId": {"type": "string"},\n          "reviews": {"@ref": "local:ReviewList"},\n          "friends": {"@ref": "local:FriendList"},\n          "orders": {"@ref": "local:OrderList"},\n        }\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["Each of those list shapes would of course contain items that reference shapes for their individual instances, like ",(0,i.jsx)(t.code,{children:"Friend"}),", ",(0,i.jsx)(t.code,{children:"Review"})," and ",(0,i.jsx)(t.code,{children:"Order"}),"."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="ReviewList example"',children:'"ReviewList": {\n    "id": "ReviewList",\n    "name": "ReviewList",\n    "title": "ReviewList",\n    "schema": {\n        "type":"object",\n        "properties": {\n            "reviews": {\n                "type":"array",\n                "items": {"@ref": "local:Review"}\n            },\n            "total": {"type":"integer"}\n        }\n    }\n}\n'})}),"\n",(0,i.jsx)(t.p,{children:"And the shapes for the individual instances would contain all the important properties and type information."}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Review example"',children:'"Review": {\n    "id": "Review",\n    "name": "Review",\n    "title": "Review",\n    "schema": {\n        "type":"object",\n        "properties": {\n            "id": {"type":"string"},\n            "title": {"type":"string"},\n            "reviewText": {"type":"string"},\n            "rating": {"type":"integer"},\n        }\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["You can also use shapes from a service as properties on your custom shapes. After you ",(0,i.jsx)(t.a,{href:"/get-started/connect-services",children:"connect a service to your project"}),", its types become shapes available to your schema. You can reference these shapes by scope using the service's ",(0,i.jsx)(t.code,{children:"id"}),", like ",(0,i.jsx)(t.code,{children:"shopify-admin:Customer"}),", or you can reference them by namespace, like ",(0,i.jsx)(t.code,{children:"Shopify_Customer"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Attaching a customer from the Shopify Admin API to the above ",(0,i.jsx)(t.code,{children:"Profile"})," shape is as simple as adding a property with an ",(0,i.jsx)(t.code,{children:'"@ref"'})," that maps to either ",(0,i.jsx)(t.code,{children:"shopify-admin:Customer"})," or ",(0,i.jsx)(t.code,{children:"Shopify_Customer"})," as shown below:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Profile with Shopify Admin Customer"',children:'"Profile": {\n    "id": "Profile",\n    "name": "Profile",\n    "title": "Profile",\n    "schema": {\n        "type": "object",\n        "properties": {\n          "shopifyCustomerData": {"@ref": "Shopify_Customer"},\n          "userId": {"type": "string"},\n          "reviews": {"@ref": "local:ReviewList"},\n          "friends": {"@ref": "local:FriendList"},\n          "orders": {"@ref": "local:OrderList"},\n        }\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["Remember that creating a shape with a property that references a shape from a service doesn't guarantee the data from that service will be on instances of that shape. In the above example, instances of the ",(0,i.jsx)(t.code,{children:"Profile"})," shape will not have any Shopify customer data at first. That data has to be mapped to the ",(0,i.jsx)(t.code,{children:"shopifyCustomerData"})," property somehow."]}),"\n",(0,i.jsxs)(t.p,{children:["To avoid manually inserting Shopify customer data every time you create a ",(0,i.jsx)(t.code,{children:"Profile"}),", you'll need to specify in your schema how customer data can be fetched. You can use ",(0,i.jsx)(t.code,{children:'"@resolver"'})," to solve this problem. Read on to the next section to find out how."]}),"\n",(0,i.jsx)(t.h3,{id:"extending-data",children:"Extending data"}),"\n",(0,i.jsxs)(t.p,{children:["You can attach data from a remote API to your shape using an ",(0,i.jsx)(t.code,{children:'"@resolver"'})," property."]}),"\n",(0,i.jsxs)(t.p,{children:["For example, you might want to create a Product shape with data that doesn't exist in Shopify, while still associating each ",(0,i.jsx)(t.code,{children:"Product"})," instance with a specific Shopify product. That is to say, you'd like to extend the ",(0,i.jsx)(t.code,{children:"Product"})," shape with Shopify product data. Prior to adding Shopify data, your ",(0,i.jsx)(t.code,{children:"Product"})," shape might look like this:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",children:'"Product":{\n    "id":"Product",\n    "title":"Product",\n    "name":"Product",\n    "schema": {\n        "type": "object",\n        "properties": {\n            "resellable": {"type": "boolean"},\n            "resalePrice": {"type": "number"}\n        }\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["The above ",(0,i.jsx)(t.code,{children:"Product"})," has a ",(0,i.jsx)(t.code,{children:'"resellable"'})," boolean that can be used to mark whether a product is available for resale, and a ",(0,i.jsx)(t.code,{children:'"resalePrice"'})," number that specifies the resale price. This is just an example of two properties that you may want that don't exist in Shopify."]}),"\n",(0,i.jsxs)(t.p,{children:["To fetch a particular product from Shopify whenever an instance of ",(0,i.jsx)(t.code,{children:"Product"})," is queried, you'd need to add two properties. One will represent the argument that will be used in the Shopify ",(0,i.jsx)(t.code,{children:"Product"})," query, and one will represent the product data Shopify will return."]}),"\n",(0,i.jsxs)(t.p,{children:["These two properties could be ",(0,i.jsx)(t.code,{children:'"shopifyProductId"'}),", to represent the ID of the product you will query, and ",(0,i.jsx)(t.code,{children:'"shopifyProductData"'})," to represent the Shopify product that will be returned."]}),"\n",(0,i.jsxs)(t.p,{children:["Your new ",(0,i.jsx)(t.code,{children:"Product"})," shape might look like this:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",children:'"Product":{\n    "id":"Product",\n    "title":"Product",\n    "name":"Product",\n    "schema": {\n        "type": "object",\n        "properties": {\n            "shopifyProductId": {"type":"string"},\n            "shopifyProductData": {\n                "@ref": "local:Sh
1opify_Product",\n                "@resolver": {\n                  "name": "graphql:query",\n                  "service": "shopify-storefront",\n                  "if": "$source.shopifyProductId",\n                  "fieldName": "product",\n                  "args": {\n                    "ops": [{"path": "id", "mapping": "$source.shopifyProductId"}]\n                  }\n                }\n            },\n            "resellable": {"type": "boolean"},\n            "resalePrice": {"type": "number"}\n        }\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["In the above example, a ",(0,i.jsx)(t.code,{children:'"shopifyProductId"'})," property has been added. The value for this string property would need to be provided in any ",(0,i.jsx)(t.code,{children:"createProduct"})," mutation that creates an instance of a ",(0,i.jsx)(t.code,{children:"Product"})," shape. If the provided ",(0,i.jsx)(t.code,{children:'"shopifyProductId"'})," value isn't valid (ex: ",(0,i.jsx)(t.code,{children:'"gid://shopify/Product/10079785100"'}),"), then the query to fetch ",(0,i.jsx)(t.code,{children:'"shopifyProductData"'})," will fail, and that field will be null on the ",(0,i.jsx)(t.code,{children:"Product"})," instance when it's fetched."]}),"\n",(0,i.jsxs)(t.p,{children:["Every time you fetch a ",(0,i.jsx)(t.code,{children:"Product"})," with a valid ",(0,i.jsx)(t.code,{children:'"shopifyProductId"'})," value, a GraphQL query to Shopify Admin's ",(0,i.jsx)(t.code,{children:"product"})," field will be executed, mapping the value of ",(0,i.jsx)(t.code,{children:'"shopifyProductId"'})," to the ",(0,i.jsx)(t.code,{children:'"id"'})," argument. This allows a Shopify product that corresponds to the product ID to be fetched and attached to each instance of the custom ",(0,i.jsx)(t.code,{children:"Product"})," shape."]}),"\n",(0,i.jsxs)(t.p,{children:["You could then run a ",(0,i.jsx)(t.code,{children:"getProduct"})," query like the one shown below. All valid fields in a ",(0,i.jsx)(t.code,{children:"product"})," query to the Shopify Admin API are available as subfields on ",(0,i.jsx)(t.code,{children:"shopifyProductData"}),"."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-graphql",children:'getProduct(_id: "abc123"){\n    shopifyProductId\n    shopifyProductData{\n        id\n        title\n        sellingPlanGroupCount\n    }\n    resellable\n    resalePrice\n}\n'})}),"\n",(0,i.jsxs)(t.admonition,{title:"Note",type:"info",children:[(0,i.jsxs)(t.p,{children:["When using ",(0,i.jsx)(t.code,{children:'"@resolver"'}),", remember that the query in the resolver only has access to data exposed in the selection set."]}),(0,i.jsxs)(t.p,{children:["In the above example, ",(0,i.jsx)(t.code,{children:"shopifyProductData"})," would return ",(0,i.jsx)(t.code,{children:"null"})," if ",(0,i.jsx)(t.code,{children:"shopifyProductId"})," was not included in the selection set."]})]}),"\n",(0,i.jsxs)(t.p,{children:["The are many more possible applications of this functionality. Using ",(0,i.jsx)(t.code,{children:'"@resolver"'})," allows you to attach a resolver to any shape property. You can learn how to configure resolvers in ",(0,i.jsx)(t.a,{href:"/schema/queries-mutations#resolvers",children:"our queries and mutations guide"}),", or in ",(0,i.jsx)(t.a,{href:"/schema/spec/#resolvers",children:"our schema spec reference"}),"."]}),"\n",(0,i.jsxs)(t.admonition,{title:"Warning",type:"caution",children:[(0,i.jsxs)(t.p,{children:["Because each ",(0,i.jsx)(t.code,{children:'"@resolver"'})," runs a query, that means every query that fetches a shape with an ",(0,i.jsx)(t.code,{children:'"@resolver"'})," property must run a nested query before it can send a response. As a result, having too many ",(0,i.jsx)(t.code,{children:'"@resolver"'})," properties can slow down queries that might otherwise be fast."]}),(0,i.jsx)(t.p,{children:"API Indexing is one solution. Query data from any API on an interval, or when a webhook fires\u2014then cache it, so you can fetch it from the ShapeDB index much faster."}),(0,i.jsxs)(t.p,{children:["Check out ",(0,i.jsx)(t.a,{href:"/schema/api-indexing-guide/",children:"our API Indexing guide"})," to learn more."]})]}),"\n",(0,i.jsxs)(t.p,{children:["You may also find yourself wanting to do the opposite by extending API data with ShapeDB data. For example, since Shopify has such a robust and well-documented API already, you may prefer to augment the data Shopify gives you with ShapeDB data. That way you can use Shopify's ",(0,i.jsx)(t.code,{children:"products"})," query to fetch a list of products, but they'll all have custom data from ShapeDB as well as the standard Shopify data."]}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:'"extends"'})," property helps you do this, and you can learn more in the next section."]}),"\n",(0,i.jsx)(t.h3,{id:"extending-api-data",children:"Extending API data"}),"\n",(0,i.jsxs)(t.p,{children:["A useful companion to ",(0,i.jsx)(t.code,{children:'"@resolver"'})," is ",(0,i.jsx)(t.code,{children:'"extends"'}),". The ",(0,i.jsx)(t.code,{children:'"@resolver"'})," property allows you to attach data from remote services to a 
1shape by querying for that data whenever an instance of that shape is fetched. You can read more about ",(0,i.jsx)(t.code,{children:'"@resolver"'})," ",(0,i.jsx)(t.a,{href:"#extending-data",children:"in the section above"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["The ",(0,i.jsx)(t.code,{children:'"extends"'})," property allows you to import all properties from a shape into a new one."]}),"\n",(0,i.jsxs)(t.p,{children:["For example, if you have a shape called ",(0,i.jsx)(t.code,{children:"Product"}),", and you want to create a new ",(0,i.jsx)(t.code,{children:"ResellableProduct"})," shape that inherits all properties from ",(0,i.jsx)(t.code,{children:"Product"}),", you could use ",(0,i.jsx)(t.code,{children:'"extends"'})," as shown below:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Basic extends example"',children:'"ResellableProduct": {\n    "schema": {\n        "extends": [\n            {"@ref":"local:Product"},\n            {\n                "type":"object",\n                "properties": {\n                    "resellable": {"type":"boolean"},\n                    "resalePrice": {"type":"number"},\n                }\n            }\n        ]\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["To extend a shape, you must use ",(0,i.jsx)(t.code,{children:'"@ref"'})," to reference it, and scope it to indicate where the shape can be found. Since we're assuming ",(0,i.jsx)(t.code,{children:"Product"})," is defined locally in your schema, you scope it with ",(0,i.jsx)(t.code,{children:"local:"}),". If ",(0,i.jsx)(t.code,{children:"Product"})," existed on an external service, like Stripe, you would scope it with the ",(0,i.jsx)(t.code,{children:"id"})," of the service, as in ",(0,i.jsx)(t.code,{children:"stripe-service-id:Product"}),". The service's ID is usually determined by the value you entered into the ",(0,i.jsx)(t.strong,{children:"Slug"})," field in the service page when first connecting it. Learn more in ",(0,i.jsx)(t.a,{href:"#using-shapes-from-connected-services",children:"the service schema section of this guide"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:'"extends"'})," also allows you to add new properties to service schema shapes. Service schema shapes don't appear in your JSON schema, but are available to be referenced by your queries, mutations, and shapes. They are derived from the types defined in any GraphQL API service you connect to your project (if the API allows introspection)."]}),"\n",(0,i.jsxs)(t.p,{children:["Let's imagine you'd like to add a new property to ",(0,i.jsx)(t.code,{children:'"Shopify_Product"'}),". This property will be available as a field when querying for products from Shopify, as if it was part of the Shopify Admin API spec."]}),"\n",(0,i.jsxs)(t.p,{children:["So if you added ",(0,i.jsx)(t.code,{children:"myCustomProperty"})," using ",(0,i.jsx)(t.code,{children:'"extends"'}),", you could then do this when querying a product from Shopify:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-graphql",metastring:'title="Shopify products query"',children:'{\n  Shopify_product(id: "abc123") {\n    myCustomProperty\n    id\n    title\n  }\n}\n'})}),"\n",(0,i.jsx)(t.admonition,{title:"Note",type:"info",children:(0,i.jsxs)(t.p,{children:["Even if you extend Shopify products with a ",(0,i.jsx)(t.code,{children:"myCustomProperty"})," property, you cannot query Shopify with only that custom property in the selection set. When querying a service API, you must always include one field in the selection set that is valid in the API's official spec."]})}),"\n",(0,i.jsxs)(t.p,{children:["Add a custom ",(0,i.jsx)(t.code,{children:'"metadata"'})," property. The property will be an object with an ",(0,i.jsx)(t.code,{children:'"externalProductionSource"'})," string attribute."]}),"\n",(0,i.jsx)(t.admonition,{title:"NOTE",type:"info",children:(0,i.jsxs)(t.p,{children:["Want to follow along with a sample project? Deploy ",(0,i.jsx)(d.A,{path:"/add-pattern?repo=https://github.com/takeshape/takeshape-starter-shopify-api-indexing",children:"the Shopify API Indexing pattern"}
1),", where everything described in this section is already implemented."]})}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.a,{href:"/services/providers/shopify/#connecting-shopify-admin-to-a-project",children:"After connecting the Shopify Admin API to your project"}),", you can add the ",(0,i.jsx)(t.code,{children:"metadata"})," property to products from Shopify by defining ",(0,i.jsx)(t.code,{children:'"Shopify_Product"'})," in your JSON schema manually. If ",(0,i.jsx)(t.code,{children:'"Shopify_Product"'})," exists in your JSON schema, then any modifications you make to the shape will be reflected in queries that return instances of the ",(0,i.jsx)(t.code,{children:'"Shopify_Product"'})," shape."]}),"\n",(0,i.jsxs)(t.p,{children:["A simple definition of ",(0,i.jsx)(t.code,{children:'"Shopify_Product"'})," should look like this:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Shopify_Product definition"',children:'"Shopify_Product": {\n    "id": "Shopify_Product",\n    "name": "Shopify_Product",\n    "title": "Shopify_Product",\n    "schema": {\n        "extends": [\n            {"@ref": "shopify-admin:Product"}\n        ]\n    }\n},\n'})}),"\n",(0,i.jsxs)(t.p,{children:["In the above example, ",(0,i.jsx)(t.code,{children:'"extends"'})," allows you to import all properties from the Shopify Admin API's ",(0,i.jsx)(t.code,{children:'"Product"'})," type. Shapes from connected services can be namespaced, like ",(0,i.jsx)(t.code,{children:'"ShopifyStorefront_Product"'})," and ",(0,i.jsx)(t.code,{children:'"BigCommerce_Product"'}),", or scoped with the ",(0,i.jsx)(t.code,{children:'"id"'})," of the service, like ",(0,i.jsx)(t.code,{children:'"shopify-admin:Product"'})," or ",(0,i.jsx)(t.code,{children:'"shipengine:Shipment"'}),". When extending a shape, you must use the service's ID to scope the shape that's being extended."]}),"\n",(0,i.jsxs)(t.p,{children:["Learn more about referencing shapes from services in ",(0,i.jsx)(t.a,{href:"#using-shapes-from-connected-services",children:"the service schema section of this guide"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["Now to add new properties to the ",(0,i.jsx)(t.code,{children:'"Shopify_Product"'})," shape, you must add a new object to the ",(0,i.jsx)(t.code,{children:'"extends"'})," array. This object should look exactly like the object that would normally appear in a Shape's schema."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Shopify_Product definition"',children:'"Shopify_Product": {\n    "id": "Shopify_Product",\n    "name": "Shopify_Product",\n    "title": "Shopify_Product",\n    "schema": {\n        "extends": [\n            {"@ref": "my-shopify-shop:Product"},\n            {\n                "type": "object",\n                "properties": {\n                    "metadata": {\n                        "type":"object",\n                        "properties": {\n                            "externalProductionSource": {\n                                "type":"string"\n                            }\n                        }\n                    }\n                }\n            }\n        ]\n    }\n},\n'})}),"\n",(0,i.jsxs)(t.p,{children:["In the above example, we've attached a ",(0,i.jsx)(t.code,{children:'"metadata"'})," property to the ",(0,i.jsx)(t.code,{children:'"Shopify_Product"'})," shape and given it an ",(0,i.jsx)(t.code,{children:'"externalProductionSource"'})," attribute. If we stopped here, this value would always be empty, because there is no way to input this data. Shopify's product creation UI does not have a ",(0,i.jsx)(t.code,{children:'"metadata"'})," category with an ",(0,i.jsx)(t.code,{children:'"externalProductionSource"'}),". Use the built-in data modeling functionality."]}),"\n",(0,i.jsxs)(t.p,{children:["You can create a ",(0,i.jsx)(t.code,{children:"ProductMetadata"})," shape ",(0,i.jsx)(t.a,{href:"#Creating-shapes-in-the-visual-editor",children:"in the shape editor"}),". Give it a ",(0,i.jsx)(t.code,{children:"productId"})," property to store the Shopify ID of the product it will be associated with. Give it an ",(0,i.jsx)(t.code,{children:"externalProductionSource"})," property that takes a string value. When you create an instance of this shape, you can provide the relevant Shopify product ID and specify the external production source."]}),"\n",(0,i.jsx)(r.A,{alt:"A screenshot of a Product Metadata shape in the admin UI",img:s(83284)}),"\n",(0,i.jsxs)(t.p,{children:["Then, to fetch a corresponding ",(0,i.jsx)(t.code,{children:"ProductMetadata"})," instance whenever a ",(0,i.jsx)(t.code,{children:"Shopify_Product"})," is fetched, you would use ",(0,i.jsx)(t.code,{children:'"extends"'})," and ",(0,i.jsx)(t.code,{children:'"@resolver"'})," together as shown below:"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Shopify_Product definition"',children:'"Shopify_Product": {\n    "id": "Shopify_Product",\n    "name": "Shopify_Product",\n    "title": "Shopify_Product",\n    "schema": {\n        "extends": [\n            {"@ref": "my-shopify-shop:Product"},\n            {\n                "type": "object",\n                "properties": {\n                    "metadata": {\n                        "@ref": "local:ProductMetadata",\n                        "@resolver": {\n                            "name": "shapedb:find",\n                            "service": "shapedb",\n                            "if": "$source.id",\n                            "shapeName": "ProductMetadata",\n                            "args": {\n                                "ops": [\n                                    {"path": "where.productId.eq", "mapping": "$source.id"}\n                                ]\n                            }\n                        }\n                    }\n                }\n            }\n        ]\n    }\n}\n'})}),"\n",(0,i.jsxs)(t.p,{children:["In the above example, the value of the ",(0,i.jsx)(t.code,{children:"id"})," field on the ",(0,i.jsx)(t.code,{children:"Shopify_Product"})," instance is being used to find an instance of ",(0,i.jsx)(t.code,{children:"ProductMetadata"})," with a matching ",(0,i.jsx)(t.code,{children:"productId"})," field value. Read about ",(0,i.jsx)(t.code,{children:'"@resolver"'})," in ",(0,i.jsx)(t.a,{href:"#extending-data",children:"the section of this guide on extending ShapeDB data"}),". Read about ",(0,i.jsx)(t.code,{children:'"extends"'})," in ",(0,i.jsx)(t.a,{href:"#extending-api-data",children:"the section of this guide on extending API data"}),"."]}),"\n",(0,i.jsxs)(t.p,{children:["With the above configuration, you could query for your custom metadata by using the ",(0,i.jsx)(t.code,{children:"Shopify_products"})," query already defined ",(0,i.jsx)(t.a,{href:"#using-shapes-from-connected-services",children:"in your Service Schema"}),":"]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-graphql",children:'{\n  Shopify_product(id: "abc123") {\n    id\n    title\n    metadata {\n      productId\n      externalProductionSource\n    }\n  }\n}\n'})}),"\n",(0,i.jsx)(t.h3,{id:"extending-to-modify-default-behaviors",children:"Extending to modify default behaviors"}),"\n",(0,i.jsxs)(t.p,{children:["In some cases you'll want to extend a Shape provided by a third-party service to alter the behavior of the\nschema in some way. In these cases it's important to restore any ",(0,i.jsx)(t.code,{children:"@mapping"})," properties that the original Shape may have\nhad, otherwise you may run into issues accessing data."]}),"\n",(0,i.jsxs)(t.p,{children:["In the following example, the ",(0,i.jsx)(t.code,{children:"@mapping"})," property is used to find episode data in the upstream request. This would\nhave been set automatically on the original shape, but now that you're extending it you will need to add it explicitly."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Extending a third-party shape"',children:'{\n  "Rick_Character": {\n    "id": "Rick_Character",\n    "name": "Rick_Character",\n    "title": "Rick Character",\n    "schema": {\n      "extends": [\n        { "@ref": "rick:Character" },\n        {\n          "type": "object",\n          "properties": {\n            "episode": {\n              "type": "array",\n              "items": {\
1n                "@ref": "rick:Episode",\n                "@mapping": "rick:Episode.episode"\n              }\n            }\n          }\n        }\n      ]\n    }\n  }\n}\n'})}),"\n",(0,i.jsx)(t.h2,{id:"examples",children:"Examples"}),"\n",(0,i.jsx)(t.h3,{id:"basic-shape-example",children:"Basic Shape example"}),"\n",(0,i.jsxs)(t.p,{children:["The most basic shape requires an ",(0,i.jsx)(t.code,{children:"id"}),", ",(0,i.jsx)(t.code,{children:"name"}),", ",(0,i.jsx)(t.code,{children:"title"})," and schema. See ",(0,i.jsx)(t.a,{href:"#configuring-a-shape-schema",children:"the section on schemas in this guide to learn more"}),"."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Basic shape config"',children:'"Product": {\n    "id": "Product",\n    "name": "Product",\n    "title": "Product",\n    "schema": {\n        "type": "object",\n        "properties": {\n            "price":{"type":"number"},\n            "title":{"type":"string"}\n        }\n    }\n}\n'})}),"\n",(0,i.jsx)(t.h3,{id:"complex-shape-example",children:"Complex Shape example"}),"\n",(0,i.jsxs)(t.p,{children:["More complex shapes make use of ",(0,i.jsx)(t.code,{children:'"extends"'})," to extend data fetched from remote services like Shopify. They also use ",(0,i.jsx)(t.code,{children:'"@resolver"'})," to define how that data is fetched."]}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Complex shape config"',children:'"ExtendedShopifyProduct": {\n    "id": "ExtendedShopifyProduct",\n    "name": "ExtendedShopifyProduct",\n    "title": "ExtendedShopifyProduct",\n    "schema": {\n        "type": "object",\n        "properties": {\n            "items": {\n                "extends": [\n                    {"@ref":"Shopify_Product"},\n                    {\n                        "type":"object",\n                        "properties": {\n                            "storefrontData": {\n                                "@ref": "shopify-storefront:Product",\n                                "@resolver": {\n                                    "name": "graphql:query",\n                                    "service": "shopify-storefront",\n                                    "if": "$source.handle",\n                                    "fieldName": "productByHandle",\n                                    "args": {\n                                        "ops": [{"path": "handle", "mapping": "$source.handle"}]\n                                    }\n                                }\n                            }\n                        }\n                    }\n                ]\n            }\n        }\n\n    }\n},\n'})}),"\n",(0,i.jsxs)(t.p,{children:["The above example defines an ",(0,i.jsx)(t.code,{children:"ExtendedShopifyProduct"})," shape that uses ",(0,i.jsx)(t.code,{children:'"@ref"'})," to inherit all properties from ",(0,i.jsx)(t.code,{children:'"Shopify_Product"'}),", then uses ",(0,i.jsx)(t.code,{children:'"@resolver"'})," to define how data from Shopify Storefront can be fetched for each instance of ",(0,i.jsx)(t.code,{children:"ExtendedShopifyProduct"}),". In this case, whenever ",(0,i.jsx)(t.code,{children:"ExtendedShopifyProduct"})," is fetched, a corresponding Shopify Storefront product with the same handle will be fetched."]}),"\n",(0,i.jsx)(t.p,{children:"This configuration is useful for composable e-commerce Shopify deployments that require the Shopify Admin API, but need Shopify Storefront's product information to create checkouts and carts."}),"\n",(0,i.jsx)(t.h3,{id:"generated-shape-example",children:"Generated Shape example"}),"\n",(0,i.jsx)(t.p,{children:"Below is an example of a shape created in the shape editor. Every time you create a shape in the editor, it will be automatically added to your project schema."}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-json",metastring:'title="Generated shape example"',children:'"Homepage": {\n    "id": "BA3ccJdIb",\n    "name": "Homepage",\n    "title": "Homepage",\n    "model": {"type": "single"},\n    "schema": {\n        "type": "object",\n        "properties": {\n            "title": {\n                "type": "string",\n                "title": "Title",\n                "minLength": 0,\n                "@mapping": "shapedb:Homepage.BJfmu0OIZ"\n            },\n            "featuredCollections": {\n                "title": "Featured Collections",\n                "@mapping": "shapedb:Homepage.SJSRmG8r7",\n                "items": {"@ref": "local:Collection"},\n                "@backreference": {"enabled": true},\n                "type": "array",\n                "@input": {\n                    "type": "array",\n                    "items": {"@ref": "local:TSRelationship"}\n                },\n                "@args": "TSRelationshipArgs",\n                "@resolver": {\n                    "name": "shapedb:getRelated",\n                    "service": "shapedb",\n                    "options": {"nullable": true}\n                }\n            },\n            "hero": {\n                "title": "Hero",\n                "@mapping": "shapedb:Homepage.ry4VnCD8W",\n                "$ref": "#/shapes/HomepageHero/schema"\n            }\n        }\n    }\n},\n'})}),"\n",(0,i.jsxs)(t.p,{children:["In general, unless you need to use ",(0,i.jsx)(t.code,{children:'"extends"'})," or ",(0,i.jsx)(t.code,{children:'"@resolver"'})," to extend your shape, it's not recommended to edit a generated shape from within the JSON editor. Changes to a generated shape from the JSON schema can render that shape no longer valid for editing in the shape editor."]}),"\n",(0,i.jsx)(o.Ay,{})]})}function m(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(u,{...e})}):u(e)}},8343:(e,t,s)=>{e.exports={src:{srcSet:s.p+"assets/ideal-img/visual-editor-new-shape.21a676b.320.png 320w,"+s.p+"assets/ideal-img/visual-editor-new-shape.488d0c4.640.png 640w,"+s.p+"assets/ideal-img/visual-editor-new-shape.4c9f90e.960.png 960w",images:[{path:s.p+"assets/ideal-img/visual-editor-new-shape.21a676b.320.png",width:320,height:262},{path:s.p+"assets/ideal-img/visual-editor-new-shape.488d0c4.640.png",width:640,height:524},{path:s.p+"assets/ideal-img/visual-editor-new-shape.4c9f90e.960.png",width:960,height:786}],src:s.p+"assets/ideal-img/visual-editor-new-shape.21a676b.320.png",toString:function(){return s.p+"assets/ideal-img/visual-editor-new-shape.21a676b.320.png"},placeholder:void 0,width:320,height:262},preSrc:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAzElEQVR4nG2PwW7CMAyG8/4vs8MOO+wN6BUuGxIUqkEp1GFxktpO4ikZk3bA0idZ1vfbshmG45tHfwC4f54v0/Y4TNvpNv/xcZthGC/XFzPObkOq6ijl75j168qactZaOf82RNSZaYa1FNXAkgJJ4SRFpE60MHOqYghhZbzHdUstSxYRrTwWKTO3JoTQGcSHSJSZRWPk5yKAbaKIJGYuMS4lpSeniWjT4qotXUpp/H9GRDpjrX2PMZ4Qce+97xGxt9b2zrnKDhFHAHj9AZ70Mpecs64MAAAAAElFTkSuQmCC"}},34959:(e,t,s)=>{e.exports={src:{srcSet:s.p+"assets/ideal-img/navigate-to-shapes-shapes.bdaf1e6.320.png 320w,"+s.p+"assets/ideal-img/navigate-to-shapes-shapes.667d1d8.640.png 640w,"+s.p+"assets/ideal-img/navigate-to-shapes-shapes.4b6aae0.960.png 960w",images:[{path:s.p+"assets/ideal-img/navigate-to-shapes-shapes.bdaf1e6.320.png",width:320,height:262},{path:s.p+"assets/ideal-img/navigate-to-shapes-shapes.667d1d8.640.png",width:640,height:524},{path:s.p+"assets/ideal-img/navigate-to-shapes-shapes.4b6aae0.960.png",width:960,height:786}],src:s.p+"assets/ideal-img/navigate-to-shapes-shapes.bdaf1e6.320.png",toString:function(){return s.p+"assets/ideal-img/navigate-to-shapes-shapes.bdaf1e6.320.png"},placeholder:void 0,width:320,height:262},preSrc:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2UlEQVR4nGWNSU7EMBBFff/LsGDBghuQbXrTBEFQUGg6HuIpKbvs33LEsKCkkr5Uv94T0zQ9OB/eP6R8/vy6Dlrp4SrVsEg1SGXOUptpvix3Qht3AoAElHUnvMwGXAq+5whE1AmlVJ9SAio4c6mr8zXnXAFUIuJWjDE+iXVde2ZGKaXUUkH73vKBSykdIcbYCWttT0QIIbRuO6I9/is24rZtrchNSUS/6pTSnzqEcGoEZi611oP2oz4UAHLOnZBSPi7LMltr34ho9N6P2pjROdf21Xt/0Vrf3wB9LDIXVUswIwAAAABJRU5ErkJggg=="}},35089:(e,t,s)=>{"use strict";s.d(t,{Ay:()=>o,RM:()=>a});var n=s(65723),i=s(16585);const a=[];function r(e){const t={a:"a",p:"p",...(0,i.R)(),...e.components};return(0,n.jsxs)(t.p,{children:["Still need help? ",(0,n.jsx)(t.a,{href:"/get-started/help",children:"Get in touch with us"}),"."]})}function o(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(r,{...e})}):r(e)}},39362:(e,t,s)=>{e.exports={src:{srcSet:s.p+"assets/ideal-img/shape-property-editing.d8dc026.320.png 320w,"+s.p+"assets/ideal-img/shape-property-editing.7dc9cfd.640.png 640w,"+s.p+"assets/ideal-img/shape-property-editing.d7252aa.960.png 960w",images:[{path:s.p+"assets/ideal-img/shape-property-editing.d8dc026.320.png",width:320,height:262},{path:s.p+"assets/ideal-img/shape-property-editing.7dc9cfd.640.png",width:640,height:524},{path:s.p+"assets/ideal-img/shape-property-editing.d7252aa.960.png",width:960,height:786}],src:s.p+"assets/ideal-img/shape-property-editing.d8dc026.320.png",toString:function(){return s.p+"assets/ideal-img/shape-property-editing.d8dc026.320.png"},placeholder:void 0,width:320,height:262},preSrc:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA10lEQVR4nEVOy2rDQAzc//+ZHnrooX8QX5NLG2heEFwnXm+8Xj1XKrYxFYyEhmFmwvV2/ch5uqT0+u66x/HZx2Mf03Kfffy6t/3tdG7fQhfHg7p74VpRzcXczVe4e51X+1ua8OjjfiZFVYHIkMhE1Qqgl
1VJmDweAXUjptZ8fZq1E4sTqzOKI6My8OAJAE1JKi1BUq6i6qjqxeJ7AiehfGIdhFYooM9sGRDQAWKIRcReI6LD2XotvY2Zea104ImrCMAyfAHDPOZ+maTpvAIAzEf2ISDuO4/sfpMwyuNx/lFYAAAAASUVORK5CYII="}},75778:(e,t,s)=>{"use strict";s.d(t,{A:()=>a});var n=s(217),i=s(65723);const a=({path:e,children:t})=>{const{siteConfig:s}=(0,n.A)(),a=s.customFields?.branding?.appUrlBase;return t?(0,i.jsx)("a",{href:`${a}${e}`,children:t}):(0,i.jsx)("a",{href:`${a}${e}`,children:a+e})}},83284:(e,t,s)=>{e.exports={src:{srcSet:s.p+"assets/ideal-img/product-metadata-shapes.962b8a8.320.png 320w,"+s.p+"assets/ideal-img/product-metadata-shapes.b9488bb.640.png 640w,"+s.p+"assets/ideal-img/product-metadata-shapes.5a07eac.960.png 960w",images:[{path:s.p+"assets/ideal-img/product-metadata-shapes.962b8a8.320.png",width:320,height:96},{path:s.p+"assets/ideal-img/product-metadata-shapes.b9488bb.640.png",width:640,height:192},{path:s.p+"assets/ideal-img/product-metadata-shapes.5a07eac.960.png",width:960,height:288}],src:s.p+"assets/ideal-img/product-metadata-shapes.962b8a8.320.png",toString:function(){return s.p+"assets/ideal-img/product-metadata-shapes.962b8a8.320.png"},placeholder:void 0,width:320,height:96},preSrc:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAIAAAAlXwkiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASUlEQVR4nDXBWxKAIAwDQO5/VUV5tKFNZsAvd8tdq7m/rbc+ATDpwNWfiEiy9DEiQtLe+/yG2TQDUM45ktZamUkySUlwNzN3/wC7UVbzC/b0GQAAAABJRU5ErkJggg=="}},90127:(e,t,s)=>{e.exports={src:{srcSet:s.p+"assets/ideal-img/shape-storage.663514e.320.png 320w,"+s.p+"assets/ideal-img/shape-storage.0866796.640.png 640w,"+s.p+"assets/ideal-img/shape-storage.fec8076.960.png 960w",images:[{path:s.p+"assets/ideal-img/shape-storage.663514e.320.png",width:320,height:262},{path:s.p+"assets/ideal-img/shape-storage.0866796.640.png",width:640,height:524},{path:s.p+"assets/ideal-img/shape-storage.fec8076.960.png",width:960,height:786}],src:s.p+"assets/ideal-img/shape-storage.663514e.320.png",toString:function(){return s.p+"assets/ideal-img/shape-storage.663514e.320.png"},placeholder:void 0,width:320,height:262},preSrc:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA2klEQVR4nD1PQU4DMQzM/z/DgQMHftC9LhdYiW6LRJeqSZN4kziJbeQUcbBGHo9nbLOezy8R4OR9+Lheb4t1brHOLzer6N4vP/bruG5Pxvv4JiKyV6LcWCqJdBYhEekixCJy2WAyPvhZu5RLr7VxwcopF66tMdbW1SSleDAAMGtTayUmEiKS3vvA1poaS4ww/QsRkVTwED1QOZ0BqDCEmVl0oDF6khr/IY9oADgYRBzPtNbHti7tGaUTa/zgEHEy97t9TSl/hxCPIca1lLJG2EfBnj5zLptz7vkX4eIy5Al6g60AAAAASUVORK5CYII="}},98853:(e,t,s)=>{e.exports={src:{srcSet:s.p+"assets/ideal-img/add-data-shapes.379ee19.320.png 320w,"+s.p+"assets/ideal-img/add-data-shapes.f6ef00b.640.png 640w,"+s.p+"assets/ideal-img/add-data-shapes.aee358c.960.png 960w",images:[{path:s.p+"assets/ideal-img/add-data-shapes.379ee19.320.png",width:320,height:96},{path:s.p+"assets/ideal-img/add-data-shapes.f6ef00b.640.png",width:640,height:191},{path:s.p+"assets/ideal-img/add-data-shapes.aee358c.960.png",width:960,height:287}],src:s.p+"assets/ideal-img/add-data-shapes.379ee19.320.png",toString:function(){return s.p+"assets/ideal-img/add-data-shapes.379ee19.320.png"},placeholder:void 0,width:320,height:96},preSrc:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAIAAAAlXwkiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAVUlEQVR4nCXBOw6AIBAFQO5/Kwvv4KcxJkpCooGFffs2UFg4E0SEo79ZRKq7O+mj+zSfx7IihQJg22uMamg/Vb3u50mp5eC9e5FWigKqShKwRprRgA9bT1TvoOr60QAAAABJRU5ErkJggg=="}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.