PageSourceSearch

https://www.windmill.dev/assets/js/13e7b542.c9c80953.js

js windmill.dev collected 2026-10-02 05:10:17 UTC 13,133 bytes, 1 lines download raw bytes

1(self.webpackChunkwindmilldocs=self.webpackChunkwindmilldocs||[]).push([["1467"],{8866(e,n,r){"use strict";r.r(n),r.d(n,{metadata:()=>d,default:()=>p,frontMatter:()=>a,contentTitle:()=>i,toc:()=>c,assets:()=>o});var d=JSON.parse('{"id":"full_code_apps/project_structure/index","title":"Project structure","description":"File layout and configuration of a full-code app in Windmill.","source":"@site/docs/full_code_apps/1_project_structure/index.mdx","sourceDirName":"full_code_apps/1_project_structure","slug":"/full_code_apps/project_structure/","permalink":"/docs/full_code_apps/project_structure/","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"description":"File layout and configuration of a full-code app in Windmill."},"sidebar":"tutorialSidebar","previous":{"title":"Full-code apps","permalink":"/docs/full_code_apps/"},"next":{"title":"Backend runnables","permalink":"/docs/full_code_apps/backend_runnables/"}}'),t=r(74848),l=r(28453),s=r(78410);let a={description:"File layout and configuration of a full-code app in Windmill."},i="Project structure",o={},c=[{value:"Directory layout",id:"directory-layout",level:2},{value:"raw_app.yaml",id:"raw_appyaml",level:2},{value:"Fields",id:"fields",level:3},{value:"Auto-generated files",id:"auto-generated-files",level:2},{value:"Files excluded from deployment",id:"files-excluded-from-deployment",level:2},{value:"App path format",id:"app-path-format",level:2}];function h(e){let n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,l.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.header,{children:(0,t.jsx)(n.h1,{id:"project-structure",children:"Project structure"})}),"\n",(0,t.jsxs)(n.p,{children:["A full-code app lives in a directory with the ",(0,t.jsx)(n.code,{children:".raw_app"})," suffix (e.g. ",(0,t.jsx)(n.code,{children:"my_app.raw_app/"}),"). If your filesystem or tooling hides dotfiles, you can use the ",(0,t.jsx)(n.code,{children:"__raw_app"})," suffix instead (e.g. ",(0,t.jsx)(n.code,{children:"my_app__raw_app/"}),") by setting ",(0,t.jsx)(n.code,{children:"nonDottedPaths: true"})," in ",(0,t.jsx)(n.code,{children:"wmill.yaml"}),"."]}),"\n",(0,t.jsxs)(n.p,{children:["All examples below use the default ",(0,t.jsx)(n.code,{children:".raw_app"})," suffix."]}),"\n",(0,t.jsx)(n.h2,{id:"directory-layout",children:"Directory layout"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"f/folder/my_app.raw_app/\n\u251C\u2500\u2500 raw_app.yaml            # App configuration (required)\n\u251C\u2500\u2500 package.json            # Frontend depen
1dencies\n\u251C\u2500\u2500 index.tsx               # Frontend entry point\n\u251C\u2500\u2500 App.tsx                 # Main component (React example)\n\u251C\u2500\u2500 index.css               # Styles\n\u251C\u2500\u2500 wmill.d.ts              # Auto-generated TypeScript definitions\n\u251C\u2500\u2500 AGENTS.md               # Auto-generated AI agent instructions\n\u251C\u2500\u2500 DATATABLES.md           # Auto-generated data table documentation\n\u251C\u2500\u2500 backend/                # Backend runnables\n\u2502   \u251C\u2500\u2500 get_users.ts        # Runnable code\n\u2502   \u251C\u2500\u2500 get_users.yaml      # Runnable config (optional)\n\u2502   \u251C\u2500\u2500 get_users.lock      # Generated lock file\n\u2502   \u2514\u2500\u2500 query.pg.sql        # SQL runnable (language inferred from extension)\n\u2514\u2500\u2500 sql_to_apply/           # SQL migrations (dev only, not deployed)\n    \u2514\u2500\u2500 001_create_table.sql\n"})}),"\n",(0,t.jsx)(n.h2,{id:"raw_appyaml",children:"raw_app.yaml"}),"\n",(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.code,{children:"raw_app.yaml"})," file is the main configuration file for a full-code app. It defines metadata, execution policy and data table access."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-yaml",children:'summary: "My dashboard app"\n\n# Optional: custom URL path (admin only)\ncustom_path: "my-dashboard"\n\n# Optional: make the app publicly accessible (no login)\npublic: false\n\n# Optional: open the app to guests (an identity required, no Windmill account needed)\nguests: false\n\n# Optional: data table access configuration\ndata:\n  datatable: "main"           # Datatable name\n  tables:                     # Tables to whitelist\n    - "users"\n    - "orders"\n  schema: "app1"              # Schema within the datatable\n'})}),"\n",(0,t.jsxs)(n.p,{children:["The execution policy (including ",(0,t.jsx)(n.code,{children:"triggerables"})," and ",(0,t.jsx)(n.code,{children:"triggerables_v2"}),") is auto-generated at deployment time and does not need to be specified in ",(0,t.jsx)(n.code,{children:"raw_app.yaml"}),".\nIts ",(0,t.jsx)(n.code,{children:"execution_mode"})," comes from ",(0,t.jsx)(n.code,{children:"public"})," and ",(0,t.jsx)(n.code,{children:"guests"}),": ",(0,t.jsx)(n.code,{children:"public: true"})," means ",(0,t.jsx)(n.code,{children:"anonymous"}),", ",(0,t.jsx)(n.code,{children:"guests: true"})," means ",(0,t.jsx)(n.code,{children:"guest"}),", and neither means ",(0,t.jsx)(n.code,{children:"publisher"}),"."]}),"\n",(0,t.jsx)(n.h3,{id:"fields",children:"Fields"}),"\n",(0,t.jsxs)(n.table,{children:[(0,t.jsx)(n.thead,{children:(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.th,{children:"Field"}),(0,t.jsx)(n.th,{children:"Type"}),(0,t.jsx)(n.th,{children:"Required"}),(0,t.jsx)(n.th,{children:"Description"})]})}),(0,t.jsxs)(n.tbody,{children:[(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"summary"})}),(0,t.jsx)(n.td,{children:"string"}),(0,t.jsx)(n.td,{children:"Yes"}),(0,t.jsx)(n.td,{children:"Short description of the app (max 1000 chars)"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"custom_path"})}),(0,t.jsx)(n.td,{children:"string"}),(0,t.jsx)(n.td,{children:"No"}),(0,t.jsx)(n.td,{children:"Custom URL path (admin only)"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"public"})}),(0,t.jsx)(n.td,{children:"boolean"}),(0,t.jsx)(n.td,{children:"No"}),(0,t.jsxs)(n.td,{children:["Whether the app is publicly accessible without login (",(0,t.jsx)(n.a,{href:"/docs/apps/public_apps",children:"public app"}),")"]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"guests"})}),(0,t.jsx)(n.td,{children:"boolean"}),(0,t.jsx)(n.td,{children:"No"}),(0,t.jsxs)(n.td,{children:["Whether the app is open to ",(0,t.jsx)(n.a,{href:"/docs/apps/guest_apps/",children:"guests"}),": an identity is required, a Windmill account is not"]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"data.datatable"})}),(0,t.jsx)(n.td,{children:"string"}),(0,t.jsx)(n.td,{children:"No"}),(0,t.jsxs)(n.td,{children:[(0,t.jsx)(n.a,{href:"/docs/core_concepts/persistent_storage/data_tables",children:"Datatable"})," name"]})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"data.tables"})}),(0,t.jsx)(n.td,{children:"string[]"}),(0,t.jsx)(n.td,{children:"No"}),(0,t.jsx)(n.td,{children:"List of table names to whitelist"})]}),(0,t.jsxs)(n.tr,{children:[(0,t.jsx)(n.td,{children:(0,t.jsx)(n.code,{children:"data.schema"})}),(0,t.jsx)(n.td,{children:"string"}),(0,t.jsx)(n.td,{children:"No"}),(0,t.jsx)(n.td,{children:"Schema name within the datatable"})]})]})]}),"\n",(0,t.jsx)(n.h2,{id:"auto-generated-files",children:"Auto-generated files"}),"\n",(0,t.jsxs)(n.p,{children:["These files are ",(0,t.jsx)(n.a,{href:"/docs/full_code_apps/cli_workflow/",children:"generated by the CLI"})," and should not be edited manually:"]}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"wmill.d.ts"})}),": TypeScript definitions for the ",(0,t.jsx)(n.code,{children:"wmill.ts"})," module, with typed signatures for each backend runnable. Regenerated 
1when runnables change during ",(0,t.jsx)(n.code,{children:"wmill app dev"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"AGENTS.md"})}),": Instructions for AI coding agents (Claude, Copilot, etc.) describing the project structure and available APIs. Generated by ",(0,t.jsx)(n.code,{children:"wmill app generate-agents"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:(0,t.jsx)(n.code,{children:"DATATABLES.md"})}),": Documentation of available data tables and their schemas. Generated by ",(0,t.jsx)(n.code,{children:"wmill app generate-agents"}),"."]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"files-excluded-from-deployment",children:"Files excluded from deployment"}),"\n",(0,t.jsx)(n.p,{children:"The following are not included when pushing to Windmill:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.code,{children:"node_modules/"})}),"\n",(0,t.jsx)(n.li,{children:(0,t.jsx)(n.code,{children:"dist/"})}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:".claude/"}),", ",(0,t.jsx)(n.code,{children:".git/"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"wmill.d.ts"}),", ",(0,t.jsx)(n.code,{children:"package-lock.json"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"raw_app.yaml"})," (metadata is handled separately)"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"sql_to_apply/"})," (dev-only migrations)"]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"DATATABLES.md"}),", ",(0,t.jsx)(n.code,{children:"AGENTS.md"})]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.code,{children:"backend/"})," (runnables are extracted and uploaded separately)"]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"app-path-format",children:"App path format"}),"\n",(0,t.jsx)(n.p,{children:"App paths follow the standard Windmill path convention:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:"<u|g|f>/<username|group|folder>/<app_name>\n"})}),"\n",(0,t.jsxs)(n.p,{children:["For example: ",(0,t.jsx)(n.code,{children:"f/dashboard/my_app"}),", ",(0,t.jsx)(n.code,{children:"u/admin/my_tool"}),", ",(0,t.jsx)(n.code,{children:"g/engineering/ops_panel"}),"."]}),"\n",(0,t.jsxs)("div",{className:"grid grid-cols-2 gap-6 mb-4",children:[(0,t.jsx)(s.A,{title:"Backend runnables",description:"Define backend scripts called from your frontend code.",href:"/docs/full_code_apps/backend_runnables",color:"orange"}),(0,t.jsx)(s.A,{title:"CLI workflow",description:"Scaffold projects with wmill app new.",href:"/docs/full_code_apps/cli_workflow",color:"orange"})]})]})}function p(e={}){let{wrapper:n}={...(0,l.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}},78410(e,n,r){"use strict";r.d(n,{A:()=>s});var d=r(74848),t=r(46942),l=r.n(t);function s({title:e,description:n,href:r,Icon:t,color:a="blue",target:i}){return(0,d.jsxs)("a",{href:r,className:l()("rounded-md p-8 border border-gray-100 dark:border-gray-800 shadow-sm transition-all cursor-pointer flex flex-col gap-2 !no-underline overflow-hidden",{blue:"hover:bg-blue-100 hover:dark:bg-blue-900/50 hover:border-blue-500 dark:hover:border-blue-600 ",teal:"hover:bg-teal-100 hover:dark:bg-teal-900/50 hover:border-teal-500 dark:hover:border-teal-600 ",orange:"hover:bg-orange-100 hover:dark:bg-orange-900/50 hover:border-orange-500 dark:hover:border-orange-600 "}[a]),target:i,children:[(0,d.jsxs)("div",{className:"flex flex-row gap-4 items-center",children:[void 0!==t&&(0,d.jsx)(t,{size:32,className:l()("rounded-md p-2",{blue:"bg-blue-100 text-blue-600 dark:bg-blue-500 dark:text-blue-100",teal:"bg-teal-100 text-teal-600 dark:bg-teal-600 dark:text-teal-100",orange:"bg-orange-200 text-orange-600 dark:bg-orange-600 dark:text-orange-100"}[a])}),(0,d.jsx)("div",{className:"text-lg font-semibold text-gray-800 dark:text-gray-100",children:e})]}),(0,d.jsx)("div",{className:"text-sm text-gray-500 dark:text-gray-200",children:n})]})}r(96540)},46942(e){!function(){"use strict";var n={}.hasOwnProperty;function r(){for(var e="",t=0;t<arguments.length;t++){var l=arguments[t];l&&(e=d(e,function(e){if("string"==typeof e||"number"==typeof e)return e;if("object"!=typeof e)return"";if(Array.isArray(e))return r.apply(null,e);if(e.toString!==Object.prototype.toString&&!e.toString.toString().includes("[native code]"))return e.toString();var t="";for(var l in e)n.call(e,l)&&e[l]&&(t=d(t,l));return t}(l)))}return e}function d(e,n){return n?e?e+" "+n:e+n:e}e.exports?(r.default=r,e.exports=r):"function"==typeof define&&"object"==typeof define.amd&&define.amd?define("classnames",[],function(){return r}):window.classNames=r}()},28453(e,n,r){"use strict";r.d(n,{R:()=>s,x:()=>a});var d=r(96540);let t={},l=d.createContext(t);function s(e){let n=d.useContext(l);return d.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:s(e.components),d.createElement(l.Provider,{value:n},e.children)}}}]);

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.