[{"data":1,"prerenderedAt":2075},["ShallowReactive",2],{"navigation-landing-en":3,"navigation-nuxt-auto-en":180,"navigation-nuxt-protokit-en":365,"blog-post-\u002Fblog\u002Fnuxt-auto-api-launch-en":507,"blog-post-surround-\u002Fblog\u002Fnuxt-auto-api-launch-en":2064},[4,8,13,28,41,51,64,77,94,110,134,150,157,172],{"title":5,"path":6,"stem":7},"Overview","\u002Fdocs\u002Flanding","0.docs\u002F1.landing\u002F001.index",{"title":9,"path":10,"stem":11,"badge":12},"Built-in Features","\u002Fdocs\u002Flanding\u002Fbuilt-in-features","0.docs\u002F1.landing\u002F002.built-in-features","New",{"title":14,"path":15,"stem":16,"children":17,"icon":27},"Content Foundation","\u002Fdocs\u002Flanding\u002Fcontent","0.docs\u002F1.landing\u002F02.content\u002F1.index",[18,19,23],{"title":5,"path":15,"stem":16},{"title":20,"path":21,"stem":22},"Details","\u002Fdocs\u002Flanding\u002Fcontent\u002Fdetails","0.docs\u002F1.landing\u002F02.content\u002F2.details",{"title":24,"path":25,"stem":26},"Technical","\u002Fdocs\u002Flanding\u002Fcontent\u002Ftechnical","0.docs\u002F1.landing\u002F02.content\u002F4.technical","i-heroicons-document-text",{"title":29,"path":30,"stem":31,"children":32,"icon":40},"Regional Content","\u002Fdocs\u002Flanding\u002Fregional","0.docs\u002F1.landing\u002F03.regional\u002F1.index",[33,34,37],{"title":5,"path":30,"stem":31},{"title":20,"path":35,"stem":36},"\u002Fdocs\u002Flanding\u002Fregional\u002Fdetails","0.docs\u002F1.landing\u002F03.regional\u002F2.details",{"title":24,"path":38,"stem":39},"\u002Fdocs\u002Flanding\u002Fregional\u002Ftechnical","0.docs\u002F1.landing\u002F03.regional\u002F4.technical","i-heroicons-globe-alt",{"title":42,"path":43,"stem":44,"children":45,"icon":50},"Multi-language","\u002Fdocs\u002Flanding\u002Fmultilang","0.docs\u002F1.landing\u002F04.multilang\u002F1.index",[46,47],{"title":5,"path":43,"stem":44},{"title":20,"path":48,"stem":49},"\u002Fdocs\u002Flanding\u002Fmultilang\u002Fdetails","0.docs\u002F1.landing\u002F04.multilang\u002F2.details","i-heroicons-language",{"title":52,"path":53,"stem":54,"children":55,"icon":63},"Blog","\u002Fdocs\u002Flanding\u002Fblog","0.docs\u002F1.landing\u002F05.blog\u002F1.index",[56,57,60],{"title":5,"path":53,"stem":54},{"title":20,"path":58,"stem":59},"\u002Fdocs\u002Flanding\u002Fblog\u002Fdetails","0.docs\u002F1.landing\u002F05.blog\u002F2.details",{"title":24,"path":61,"stem":62},"\u002Fdocs\u002Flanding\u002Fblog\u002Ftechnical","0.docs\u002F1.landing\u002F05.blog\u002F4.technical","i-heroicons-pencil-square",{"title":65,"path":66,"stem":67,"children":68,"icon":76},"Documentation","\u002Fdocs\u002Flanding\u002Fdocs","0.docs\u002F1.landing\u002F06.docs\u002F1.index",[69,70,73],{"title":5,"path":66,"stem":67},{"title":20,"path":71,"stem":72},"\u002Fdocs\u002Flanding\u002Fdocs\u002Fdetails","0.docs\u002F1.landing\u002F06.docs\u002F2.details",{"title":24,"path":74,"stem":75},"\u002Fdocs\u002Flanding\u002Fdocs\u002Ftechnical","0.docs\u002F1.landing\u002F06.docs\u002F4.technical","i-heroicons-book-open",{"title":78,"path":79,"stem":80,"children":81,"icon":93},"Forms","\u002Fdocs\u002Flanding\u002Fforms","0.docs\u002F1.landing\u002F07.forms\u002F1.index",[82,83,86,90],{"title":5,"path":79,"stem":80},{"title":20,"path":84,"stem":85},"\u002Fdocs\u002Flanding\u002Fforms\u002Fdetails","0.docs\u002F1.landing\u002F07.forms\u002F2.details",{"title":87,"path":88,"stem":89},"Admin","\u002Fdocs\u002Flanding\u002Fforms\u002Fadmin","0.docs\u002F1.landing\u002F07.forms\u002F3.admin",{"title":24,"path":91,"stem":92},"\u002Fdocs\u002Flanding\u002Fforms\u002Ftechnical","0.docs\u002F1.landing\u002F07.forms\u002F4.technical","i-heroicons-clipboard-document-list",{"title":95,"path":96,"stem":97,"children":98,"icon":109},"Email","\u002Fdocs\u002Flanding\u002Femail","0.docs\u002F1.landing\u002F08.email\u002F1.index",[99,100,103,106],{"title":5,"path":96,"stem":97},{"title":20,"path":101,"stem":102},"\u002Fdocs\u002Flanding\u002Femail\u002Fdetails","0.docs\u002F1.landing\u002F08.email\u002F2.details",{"title":87,"path":104,"stem":105},"\u002Fdocs\u002Flanding\u002Femail\u002Fadmin","0.docs\u002F1.landing\u002F08.email\u002F3.admin",{"title":24,"path":107,"stem":108},"\u002Fdocs\u002Flanding\u002Femail\u002Ftechnical","0.docs\u002F1.landing\u002F08.email\u002F4.technical","i-heroicons-envelope",{"title":111,"path":112,"stem":113,"children":114,"icon":133},"Feedback Platform","\u002Fdocs\u002Flanding\u002Ffeedback","0.docs\u002F1.landing\u002F09.feedback\u002F1.index",[115,116,119,122,125,129],{"title":5,"path":112,"stem":113},{"title":20,"path":117,"stem":118},"\u002Fdocs\u002Flanding\u002Ffeedback\u002Fdetails","0.docs\u002F1.landing\u002F09.feedback\u002F2.details",{"title":87,"path":120,"stem":121},"\u002Fdocs\u002Flanding\u002Ffeedback\u002Fadmin","0.docs\u002F1.landing\u002F09.feedback\u002F3.admin",{"title":24,"path":123,"stem":124},"\u002Fdocs\u002Flanding\u002Ffeedback\u002Ftechnical","0.docs\u002F1.landing\u002F09.feedback\u002F4.technical",{"title":126,"path":127,"stem":128},"Compare vs SaaS","\u002Fdocs\u002Flanding\u002Ffeedback\u002Fcompare","0.docs\u002F1.landing\u002F09.feedback\u002F5.compare",{"title":130,"path":131,"stem":132},"FAQ","\u002Fdocs\u002Flanding\u002Ffeedback\u002Ffaq","0.docs\u002F1.landing\u002F09.feedback\u002F6.faq","i-heroicons-chat-bubble-left-right",{"title":135,"path":136,"stem":137,"children":138,"icon":149},"Storage","\u002Fdocs\u002Flanding\u002Fstorage","0.docs\u002F1.landing\u002F10.storage\u002F1.index",[139,140,143,146],{"title":5,"path":136,"stem":137},{"title":20,"path":141,"stem":142},"\u002Fdocs\u002Flanding\u002Fstorage\u002Fdetails","0.docs\u002F1.landing\u002F10.storage\u002F2.details",{"title":87,"path":144,"stem":145},"\u002Fdocs\u002Flanding\u002Fstorage\u002Fadmin","0.docs\u002F1.landing\u002F10.storage\u002F3.admin",{"title":24,"path":147,"stem":148},"\u002Fdocs\u002Flanding\u002Fstorage\u002Ftechnical","0.docs\u002F1.landing\u002F10.storage\u002F4.technical","i-heroicons-circle-stack",{"title":151,"path":152,"stem":153,"children":154,"icon":156},"Offline First","\u002Fdocs\u002Flanding\u002Foffline-first","0.docs\u002F1.landing\u002F11.offline-first\u002F1.index",[155],{"title":151,"path":152,"stem":153},"i-heroicons-users",{"title":158,"path":159,"stem":160,"children":161,"icon":156},"Yjs Sync","\u002Fdocs\u002Flanding\u002Fyjs-sync","0.docs\u002F1.landing\u002F12.yjs-sync\u002F1.index",[162,163,166,169],{"title":5,"path":159,"stem":160},{"title":20,"path":164,"stem":165},"\u002Fdocs\u002Flanding\u002Fyjs-sync\u002Fdetails","0.docs\u002F1.landing\u002F12.yjs-sync\u002F2.details",{"title":87,"path":167,"stem":168},"\u002Fdocs\u002Flanding\u002Fyjs-sync\u002Fadmin","0.docs\u002F1.landing\u002F12.yjs-sync\u002F3.admin",{"title":24,"path":170,"stem":171},"\u002Fdocs\u002Flanding\u002Fyjs-sync\u002Ftechnical","0.docs\u002F1.landing\u002F12.yjs-sync\u002F4.technical",{"title":173,"path":174,"stem":175,"children":176,"badge":178,"icon":179},"Newsletter","\u002Fdocs\u002Flanding\u002Fnewsletter","0.docs\u002F1.landing\u002F13.newsletter\u002Findex",[177],{"title":173,"path":174,"stem":175,"badge":178},"Coming Soon","i-lucide-send",[181,184,202,318,358],{"title":5,"path":182,"stem":183},"\u002Fdocs\u002Fnuxt-auto","0.docs\u002F3.nuxt-auto\u002Findex",{"title":185,"path":186,"stem":187,"children":188,"page":201},"Getting Started","\u002Fdocs\u002Fnuxt-auto\u002Fgetting-started","0.docs\u002F3.nuxt-auto\u002F1.getting-started",[189,193,197],{"title":190,"path":191,"stem":192},"Introduction","\u002Fdocs\u002Fnuxt-auto\u002Fgetting-started\u002Fintroduction","0.docs\u002F3.nuxt-auto\u002F1.getting-started\u002F1.introduction",{"title":194,"path":195,"stem":196},"Installation","\u002Fdocs\u002Fnuxt-auto\u002Fgetting-started\u002Finstallation","0.docs\u002F3.nuxt-auto\u002F1.getting-started\u002F2.installation",{"title":198,"path":199,"stem":200},"Quick Start","\u002Fdocs\u002Fnuxt-auto\u002Fgetting-started\u002Fquick-start","0.docs\u002F3.nuxt-auto\u002F1.getting-started\u002F3.quick-start",false,{"title":203,"path":204,"stem":205,"children":206,"page":201},"Auto API","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api","0.docs\u002F3.nuxt-auto\u002F2.auto-api",[207,210,214,218,222,226,230,234,238,242,246,250,254,258,262,266,270,274,278,282,286,290,294,298,302,306,310,314],{"title":185,"path":208,"stem":209},"\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fgetting-started","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F1.getting-started",{"title":211,"path":212,"stem":213},"Aggregations","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Faggregations","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F10.aggregations",{"title":215,"path":216,"stem":217},"Lifecycle Hooks","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Flifecycle-hooks","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F11.lifecycle-hooks",{"title":219,"path":220,"stem":221},"Many-to-Many (M2M) Relationships","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fm2m-relationships","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F12.m2m-relationships",{"title":223,"path":224,"stem":225},"Plugin System","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fplugin-system","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F13.plugin-system",{"title":227,"path":228,"stem":229},"Database Adapters","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fdatabase-adapters","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F14.database-adapters",{"title":231,"path":232,"stem":233},"Custom Endpoints","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fcustom-endpoints","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F15.custom-endpoints",{"title":235,"path":236,"stem":237},"Multi-Tenancy","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fmulti-tenancy","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F16.multi-tenancy",{"title":239,"path":240,"stem":241},"Schema Presets (Cross-engine)","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fschema-presets","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F17.schema-presets",{"title":243,"path":244,"stem":245},"Permissions Cookbook","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fpermissions-cookbook","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F18.permissions-cookbook",{"title":247,"path":248,"stem":249},"Billing & Plan Gating","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fbilling","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F19.billing",{"title":251,"path":252,"stem":253},"Validation","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fvalidation","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F2.validation",{"title":255,"path":256,"stem":257},"Rate Limiting","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Frate-limiting","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F20.rate-limiting",{"title":259,"path":260,"stem":261},"Request Metadata Plugin","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Frequest-metadata","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F21.request-metadata",{"title":263,"path":264,"stem":265},"Plugin Catalog","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fplugin-catalog","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F22.plugin-catalog",{"title":267,"path":268,"stem":269},"Configuration Reference","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fconfiguration","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F23.configuration",{"title":271,"path":272,"stem":273},"Handler Overrides","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fhandler-overrides","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F3.handler-overrides",{"title":275,"path":276,"stem":277},"Cloudflare D1","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fcloudflare-d1","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F30.cloudflare-d1",{"title":279,"path":280,"stem":281},"SQLite to D1 Migration","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fmigration-sqlite-d1","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F31.migration-sqlite-d1",{"title":283,"path":284,"stem":285},"Frontend Composables","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Ffrontend-composables","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F32.frontend-composables",{"title":287,"path":288,"stem":289},"Testing","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Ftesting","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F33.testing",{"title":291,"path":292,"stem":293},"Pagination","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fpagination","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F4.pagination",{"title":295,"path":296,"stem":297},"Upgrading","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fupgrading","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F40.upgrading",{"title":299,"path":300,"stem":301},"Soft Deletes","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fsoft-deletes","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F5.soft-deletes",{"title":303,"path":304,"stem":305},"Authentication & Authorization","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fauthentication-authorization","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F6.authentication-authorization",{"title":307,"path":308,"stem":309},"Better-Auth Integration","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fbetter-auth","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F7.better-auth",{"title":311,"path":312,"stem":313},"Nested Relations","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fnested-relationships","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F8.nested-relationships",{"title":315,"path":316,"stem":317},"Bulk Operations","\u002Fdocs\u002Fnuxt-auto\u002Fauto-api\u002Fbulk-operations","0.docs\u002F3.nuxt-auto\u002F2.auto-api\u002F9.bulk-operations",{"title":319,"path":320,"stem":321,"children":322,"page":201},"Auto Admin","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin","0.docs\u002F3.nuxt-auto\u002F3.auto-admin",[323,326,330,334,338,342,346,350,354],{"title":185,"path":324,"stem":325},"\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fgetting-started","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F1.getting-started",{"title":327,"path":328,"stem":329},"Configuration & Theming","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fconfiguration-theming","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F2.configuration-theming",{"title":331,"path":332,"stem":333},"Resource Configuration","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fresource-configuration","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F3.resource-configuration",{"title":335,"path":336,"stem":337},"Form Fields & Widgets","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fform-fields-widgets","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F4.form-fields-widgets",{"title":339,"path":340,"stem":341},"Permissions","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fpermissions","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F5.permissions",{"title":343,"path":344,"stem":345},"Custom Pages","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fcustom-pages","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F6.custom-pages",{"title":347,"path":348,"stem":349},"M2M Relationships","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fm2m-relationships","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F7.m2m-relationships",{"title":351,"path":352,"stem":353},"Custom Actions","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fcustom-actions","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F8.custom-actions",{"title":355,"path":356,"stem":357},"Composables","\u002Fdocs\u002Fnuxt-auto\u002Fauto-admin\u002Fcomposables","0.docs\u002F3.nuxt-auto\u002F3.auto-admin\u002F9.composables",{"title":359,"path":360,"stem":361,"children":362,"icon":364},"Claude Code Skills","\u002Fdocs\u002Fnuxt-auto\u002Fclaude-code-skills","0.docs\u002F3.nuxt-auto\u002F4.claude-code-skills\u002F1.index",[363],{"title":5,"path":360,"stem":361},"i-lucide-bot",[366,369,382,409,427,450,472,502],{"title":5,"path":367,"stem":368},"\u002Fdocs\u002Fnuxt-protokit","0.docs\u002F4.nuxt-protokit\u002Findex",{"title":185,"path":370,"stem":371,"children":372,"icon":381},"\u002Fdocs\u002Fnuxt-protokit\u002Fgetting-started","0.docs\u002F4.nuxt-protokit\u002F1.getting-started\u002F1.index",[373,374,378],{"title":190,"path":370,"stem":371},{"title":375,"path":376,"stem":377},"Core Concepts","\u002Fdocs\u002Fnuxt-protokit\u002Fgetting-started\u002Fconcepts","0.docs\u002F4.nuxt-protokit\u002F1.getting-started\u002F2.concepts",{"title":198,"path":379,"stem":380},"\u002Fdocs\u002Fnuxt-protokit\u002Fgetting-started\u002Fquick-start","0.docs\u002F4.nuxt-protokit\u002F1.getting-started\u002F3.quick-start","i-lucide-rocket",{"title":383,"path":384,"stem":385,"children":386,"icon":408},"Schemas","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F1.index",[387,388,392,396,400,404],{"title":5,"path":384,"stem":385},{"title":389,"path":390,"stem":391},"Field Types","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Ffields","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F2.fields",{"title":393,"path":394,"stem":395},"Collections","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Fcollections","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F3.collections",{"title":397,"path":398,"stem":399},"Derived & Computed","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Fderived-computed","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F4.derived-computed",{"title":401,"path":402,"stem":403},"Connections","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Fconnections","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F5.connections",{"title":405,"path":406,"stem":407},"Visualizations & Layouts","\u002Fdocs\u002Fnuxt-protokit\u002Fschemas\u002Fvisualizations","0.docs\u002F4.nuxt-protokit\u002F2.schemas\u002F6.visualizations","i-lucide-file-code",{"title":355,"path":410,"stem":411,"children":412,"icon":426},"\u002Fdocs\u002Fnuxt-protokit\u002Fcomposables","0.docs\u002F4.nuxt-protokit\u002F3.composables\u002F1.index",[413,414,418,422],{"title":5,"path":410,"stem":411},{"title":415,"path":416,"stem":417},"usePrototype","\u002Fdocs\u002Fnuxt-protokit\u002Fcomposables\u002Fuse-prototype","0.docs\u002F4.nuxt-protokit\u002F3.composables\u002F2.use-prototype",{"title":419,"path":420,"stem":421},"useProtoDoc","\u002Fdocs\u002Fnuxt-protokit\u002Fcomposables\u002Fuse-proto-doc","0.docs\u002F4.nuxt-protokit\u002F3.composables\u002F3.use-proto-doc",{"title":423,"path":424,"stem":425},"useProtoCollection","\u002Fdocs\u002Fnuxt-protokit\u002Fcomposables\u002Fuse-proto-collection","0.docs\u002F4.nuxt-protokit\u002F3.composables\u002F4.use-proto-collection","i-lucide-layers",{"title":428,"path":429,"stem":430,"children":431,"icon":449},"Components","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F1.index",[432,433,437,441,445],{"title":5,"path":429,"stem":430},{"title":434,"path":435,"stem":436},"ProtoTool","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents\u002Fproto-tool","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F2.proto-tool",{"title":438,"path":439,"stem":440},"ProtoCrudModal","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents\u002Fproto-crud-modal","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F3.proto-crud-modal",{"title":442,"path":443,"stem":444},"ProtoCalendar","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents\u002Fproto-calendar","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F4.proto-calendar",{"title":446,"path":447,"stem":448},"ProtoItemPanel variants","\u002Fdocs\u002Fnuxt-protokit\u002Fcomponents\u002Fproto-item-panels","0.docs\u002F4.nuxt-protokit\u002F4.components\u002F5.proto-item-panels","i-lucide-puzzle",{"title":151,"path":451,"stem":452,"children":453,"icon":471},"\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F1.index",[454,455,459,463,467],{"title":151,"path":451,"stem":452},{"title":456,"path":457,"stem":458},"Corruption Recovery","\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first\u002Fcorruption-recovery","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F2.corruption-recovery",{"title":460,"path":461,"stem":462},"Size & GC","\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first\u002Fdocument-size-and-gc","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F3.document-size-and-gc",{"title":464,"path":465,"stem":466},"Multi-tenant Isolation","\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first\u002Fmulti-tenant-isolation","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F4.multi-tenant-isolation",{"title":468,"path":469,"stem":470},"Encryption at Rest","\u002Fdocs\u002Fnuxt-protokit\u002Foffline-first\u002Fencryption","0.docs\u002F4.nuxt-protokit\u002F5.offline-first\u002F5.encryption","i-lucide-wifi-off",{"title":473,"icon":474,"path":475,"stem":476,"children":477},"Advanced","i-lucide-graduation-cap","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F1.index",[478,479,483,487,491,494,498],{"title":5,"path":475,"stem":476},{"title":480,"path":481,"stem":482},"Multi-Tool Apps","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fbuilding-a-toolkit","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F1.building-a-toolkit",{"title":484,"path":485,"stem":486},"Schema Patterns","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fcustom-schema-patterns","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F2.custom-schema-patterns",{"title":488,"path":489,"stem":490},"Custom Fields & Viz","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fextensibility","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F3.extensibility",{"title":339,"path":492,"stem":493},"\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fpermissions","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F4.permissions",{"title":495,"path":496,"stem":497},"Business Logic Guards","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fpermissions-business-logic","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F5.permissions-business-logic",{"title":499,"path":500,"stem":501},"Headless Mode","\u002Fdocs\u002Fnuxt-protokit\u002Fadvanced\u002Fheadless-mode","0.docs\u002F4.nuxt-protokit\u002F6.advanced\u002F6.headless-mode",{"title":359,"path":503,"stem":504,"children":505,"icon":364},"\u002Fdocs\u002Fnuxt-protokit\u002Fclaude-code-skills","0.docs\u002F4.nuxt-protokit\u002F7.claude-code-skills\u002F1.index",[506],{"title":5,"path":503,"stem":504},{"id":508,"title":509,"authors":510,"badge":515,"body":517,"date":2048,"description":2049,"extension":2050,"image":2051,"meta":2053,"navigation":610,"ogImage":2054,"path":2055,"published":610,"seo":2056,"stem":2057,"tags":2058,"__hash__":2063},"posts\u002F3.blog\u002F10.nuxt-auto-api-launch.md","nuxt-auto: a secure REST API from your Drizzle schema",[511],{"name":512,"avatar":513},"Benjamin",{"src":514},"profile\u002Fbenjamin.webp",{"label":516},"Launch",{"type":518,"value":519,"toc":2039},"minimark",[520,525,529,532,544,548,551,934,937,1175,1178,1484,1487,1519,1523,1534,1537,1541,1544,1813,1820,1824,1846,1850,1853,1871,1874,1877,1881,1904,1907,2014,2032,2035],[521,522,524],"h2",{"id":523},"why","Why",[526,527,528],"p",{},"Too much boilerplate. When I moved to Nuxt for full-stack work, auth was the easy part, Better Auth handles that. The backend was the problem. Every resource meant a new set of endpoints, each with its own permission checks, each tested by hand. I tried oRPC to get a cleaner backend with less of it. Didn't get me there.",[526,530,531],{},"I maintain 10+ layers and modules on the same stack, so every endpoint I don't have to write adds up. What I wanted: register the table, get the API. And an admin, if I need one.",[526,533,534,535,539,540,543],{},"So I built nuxt-auto. ",[536,537,538],"code",{},"@websideproject\u002Fnuxt-auto-api"," and ",[536,541,542],{},"@websideproject\u002Fnuxt-auto-admin"," are on 0.1.0 now, the first stable release.",[521,545,547],{"id":546},"what-it-looks-like","What it looks like",[526,549,550],{},"A normal Drizzle table:",[552,553,559],"pre",{"className":554,"code":555,"filename":556,"language":557,"meta":558,"style":558},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { integer, sqliteTable, text } from 'drizzle-orm\u002Fsqlite-core'\n\nexport const posts = sqliteTable('posts', {\n  id: integer('id').primaryKey({ autoIncrement: true }),\n  title: text('title').notNull(),\n  slug: text('slug').notNull().unique(),\n  content: text('content'),\n  published: integer('published', { mode: 'boolean' }).default(false),\n  authorId: text('author_id'),\n  createdAt: integer('created_at', { mode: 'timestamp' }).$defaultFn(() => new Date()),\n})\n","server\u002Fdatabase\u002Fschema.ts","ts","",[536,560,561,605,612,647,698,729,765,788,841,864,925],{"__ignoreMap":558},[562,563,566,570,574,578,581,584,586,589,592,595,598,602],"span",{"class":564,"line":565},"line",1,[562,567,569],{"class":568},"s7zQu","import",[562,571,573],{"class":572},"sMK4o"," {",[562,575,577],{"class":576},"sTEyZ"," integer",[562,579,580],{"class":572},",",[562,582,583],{"class":576}," sqliteTable",[562,585,580],{"class":572},[562,587,588],{"class":576}," text",[562,590,591],{"class":572}," }",[562,593,594],{"class":568}," from",[562,596,597],{"class":572}," '",[562,599,601],{"class":600},"sfazB","drizzle-orm\u002Fsqlite-core",[562,603,604],{"class":572},"'\n",[562,606,608],{"class":564,"line":607},2,[562,609,611],{"emptyLinePlaceholder":610},true,"\n",[562,613,615,618,622,625,628,631,634,637,640,642,644],{"class":564,"line":614},3,[562,616,617],{"class":568},"export",[562,619,621],{"class":620},"spNyl"," const",[562,623,624],{"class":576}," posts ",[562,626,627],{"class":572},"=",[562,629,583],{"class":630},"s2Zo4",[562,632,633],{"class":576},"(",[562,635,636],{"class":572},"'",[562,638,639],{"class":600},"posts",[562,641,636],{"class":572},[562,643,580],{"class":572},[562,645,646],{"class":572}," {\n",[562,648,650,654,657,659,661,663,666,668,671,674,677,679,682,685,687,691,693,695],{"class":564,"line":649},4,[562,651,653],{"class":652},"swJcz","  id",[562,655,656],{"class":572},":",[562,658,577],{"class":630},[562,660,633],{"class":576},[562,662,636],{"class":572},[562,664,665],{"class":600},"id",[562,667,636],{"class":572},[562,669,670],{"class":576},")",[562,672,673],{"class":572},".",[562,675,676],{"class":630},"primaryKey",[562,678,633],{"class":576},[562,680,681],{"class":572},"{",[562,683,684],{"class":652}," autoIncrement",[562,686,656],{"class":572},[562,688,690],{"class":689},"sfNiH"," true",[562,692,591],{"class":572},[562,694,670],{"class":576},[562,696,697],{"class":572},",\n",[562,699,701,704,706,708,710,712,715,717,719,721,724,727],{"class":564,"line":700},5,[562,702,703],{"class":652},"  title",[562,705,656],{"class":572},[562,707,588],{"class":630},[562,709,633],{"class":576},[562,711,636],{"class":572},[562,713,714],{"class":600},"title",[562,716,636],{"class":572},[562,718,670],{"class":576},[562,720,673],{"class":572},[562,722,723],{"class":630},"notNull",[562,725,726],{"class":576},"()",[562,728,697],{"class":572},[562,730,732,735,737,739,741,743,746,748,750,752,754,756,758,761,763],{"class":564,"line":731},6,[562,733,734],{"class":652},"  slug",[562,736,656],{"class":572},[562,738,588],{"class":630},[562,740,633],{"class":576},[562,742,636],{"class":572},[562,744,745],{"class":600},"slug",[562,747,636],{"class":572},[562,749,670],{"class":576},[562,751,673],{"class":572},[562,753,723],{"class":630},[562,755,726],{"class":576},[562,757,673],{"class":572},[562,759,760],{"class":630},"unique",[562,762,726],{"class":576},[562,764,697],{"class":572},[562,766,768,771,773,775,777,779,782,784,786],{"class":564,"line":767},7,[562,769,770],{"class":652},"  content",[562,772,656],{"class":572},[562,774,588],{"class":630},[562,776,633],{"class":576},[562,778,636],{"class":572},[562,780,781],{"class":600},"content",[562,783,636],{"class":572},[562,785,670],{"class":576},[562,787,697],{"class":572},[562,789,791,794,796,798,800,802,805,807,809,811,814,816,818,821,823,825,827,829,832,834,837,839],{"class":564,"line":790},8,[562,792,793],{"class":652},"  published",[562,795,656],{"class":572},[562,797,577],{"class":630},[562,799,633],{"class":576},[562,801,636],{"class":572},[562,803,804],{"class":600},"published",[562,806,636],{"class":572},[562,808,580],{"class":572},[562,810,573],{"class":572},[562,812,813],{"class":652}," mode",[562,815,656],{"class":572},[562,817,597],{"class":572},[562,819,820],{"class":600},"boolean",[562,822,636],{"class":572},[562,824,591],{"class":572},[562,826,670],{"class":576},[562,828,673],{"class":572},[562,830,831],{"class":630},"default",[562,833,633],{"class":576},[562,835,836],{"class":689},"false",[562,838,670],{"class":576},[562,840,697],{"class":572},[562,842,844,847,849,851,853,855,858,860,862],{"class":564,"line":843},9,[562,845,846],{"class":652},"  authorId",[562,848,656],{"class":572},[562,850,588],{"class":630},[562,852,633],{"class":576},[562,854,636],{"class":572},[562,856,857],{"class":600},"author_id",[562,859,636],{"class":572},[562,861,670],{"class":576},[562,863,697],{"class":572},[562,865,867,870,872,874,876,878,881,883,885,887,889,891,893,896,898,900,902,904,907,909,911,914,917,920,923],{"class":564,"line":866},10,[562,868,869],{"class":652},"  createdAt",[562,871,656],{"class":572},[562,873,577],{"class":630},[562,875,633],{"class":576},[562,877,636],{"class":572},[562,879,880],{"class":600},"created_at",[562,882,636],{"class":572},[562,884,580],{"class":572},[562,886,573],{"class":572},[562,888,813],{"class":652},[562,890,656],{"class":572},[562,892,597],{"class":572},[562,894,895],{"class":600},"timestamp",[562,897,636],{"class":572},[562,899,591],{"class":572},[562,901,670],{"class":576},[562,903,673],{"class":572},[562,905,906],{"class":630},"$defaultFn",[562,908,633],{"class":576},[562,910,726],{"class":572},[562,912,913],{"class":620}," =>",[562,915,916],{"class":572}," new",[562,918,919],{"class":630}," Date",[562,921,922],{"class":576},"())",[562,924,697],{"class":572},[562,926,928,931],{"class":564,"line":927},11,[562,929,930],{"class":572},"}",[562,932,933],{"class":576},")\n",[526,935,936],{},"The rules. Anything you don't list is refused:",[552,938,941],{"className":554,"code":939,"filename":940,"language":557,"meta":558,"style":558},"import type { ResourceAuthConfig } from '@websideproject\u002Fnuxt-auto-api'\nimport { eq } from 'drizzle-orm'\n\nexport const postsAuth: ResourceAuthConfig = {\n  permissions: {\n    read: true, \u002F\u002F everyone\n    create: 'posts:write', \u002F\u002F callers with this permission\n    update: 'posts:write',\n    delete: 'posts:publish',\n  },\n  \u002F\u002F readers don't see drafts at all (404, not 403)\n  listFilter: (posts, ctx) =>\n    ctx.permissions.includes('posts:write') ? undefined : eq(posts.published, true),\n}\n","modules\u002Fblog\u002Fauth.ts",[536,942,943,965,985,989,1008,1017,1032,1051,1066,1082,1087,1092,1116,1169],{"__ignoreMap":558},[562,944,945,947,950,952,955,957,959,961,963],{"class":564,"line":565},[562,946,569],{"class":568},[562,948,949],{"class":568}," type",[562,951,573],{"class":572},[562,953,954],{"class":576}," ResourceAuthConfig",[562,956,591],{"class":572},[562,958,594],{"class":568},[562,960,597],{"class":572},[562,962,538],{"class":600},[562,964,604],{"class":572},[562,966,967,969,971,974,976,978,980,983],{"class":564,"line":607},[562,968,569],{"class":568},[562,970,573],{"class":572},[562,972,973],{"class":576}," eq",[562,975,591],{"class":572},[562,977,594],{"class":568},[562,979,597],{"class":572},[562,981,982],{"class":600},"drizzle-orm",[562,984,604],{"class":572},[562,986,987],{"class":564,"line":614},[562,988,611],{"emptyLinePlaceholder":610},[562,990,991,993,995,998,1000,1003,1006],{"class":564,"line":649},[562,992,617],{"class":568},[562,994,621],{"class":620},[562,996,997],{"class":576}," postsAuth",[562,999,656],{"class":572},[562,1001,954],{"class":1002},"sBMFI",[562,1004,1005],{"class":572}," =",[562,1007,646],{"class":572},[562,1009,1010,1013,1015],{"class":564,"line":700},[562,1011,1012],{"class":652},"  permissions",[562,1014,656],{"class":572},[562,1016,646],{"class":572},[562,1018,1019,1022,1024,1026,1028],{"class":564,"line":731},[562,1020,1021],{"class":652},"    read",[562,1023,656],{"class":572},[562,1025,690],{"class":689},[562,1027,580],{"class":572},[562,1029,1031],{"class":1030},"sHwdD"," \u002F\u002F everyone\n",[562,1033,1034,1037,1039,1041,1044,1046,1048],{"class":564,"line":767},[562,1035,1036],{"class":652},"    create",[562,1038,656],{"class":572},[562,1040,597],{"class":572},[562,1042,1043],{"class":600},"posts:write",[562,1045,636],{"class":572},[562,1047,580],{"class":572},[562,1049,1050],{"class":1030}," \u002F\u002F callers with this permission\n",[562,1052,1053,1056,1058,1060,1062,1064],{"class":564,"line":790},[562,1054,1055],{"class":652},"    update",[562,1057,656],{"class":572},[562,1059,597],{"class":572},[562,1061,1043],{"class":600},[562,1063,636],{"class":572},[562,1065,697],{"class":572},[562,1067,1068,1071,1073,1075,1078,1080],{"class":564,"line":843},[562,1069,1070],{"class":652},"    delete",[562,1072,656],{"class":572},[562,1074,597],{"class":572},[562,1076,1077],{"class":600},"posts:publish",[562,1079,636],{"class":572},[562,1081,697],{"class":572},[562,1083,1084],{"class":564,"line":866},[562,1085,1086],{"class":572},"  },\n",[562,1088,1089],{"class":564,"line":927},[562,1090,1091],{"class":1030},"  \u002F\u002F readers don't see drafts at all (404, not 403)\n",[562,1093,1095,1098,1100,1103,1106,1108,1111,1113],{"class":564,"line":1094},12,[562,1096,1097],{"class":630},"  listFilter",[562,1099,656],{"class":572},[562,1101,1102],{"class":572}," (",[562,1104,639],{"class":1105},"sHdIc",[562,1107,580],{"class":572},[562,1109,1110],{"class":1105}," ctx",[562,1112,670],{"class":572},[562,1114,1115],{"class":620}," =>\n",[562,1117,1119,1122,1124,1127,1129,1132,1134,1136,1138,1140,1143,1146,1149,1152,1154,1157,1159,1161,1163,1165,1167],{"class":564,"line":1118},13,[562,1120,1121],{"class":576},"    ctx",[562,1123,673],{"class":572},[562,1125,1126],{"class":576},"permissions",[562,1128,673],{"class":572},[562,1130,1131],{"class":630},"includes",[562,1133,633],{"class":576},[562,1135,636],{"class":572},[562,1137,1043],{"class":600},[562,1139,636],{"class":572},[562,1141,1142],{"class":576},") ",[562,1144,1145],{"class":572},"?",[562,1147,1148],{"class":572}," undefined",[562,1150,1151],{"class":572}," :",[562,1153,973],{"class":630},[562,1155,1156],{"class":576},"(posts",[562,1158,673],{"class":572},[562,1160,804],{"class":576},[562,1162,580],{"class":572},[562,1164,690],{"class":689},[562,1166,670],{"class":576},[562,1168,697],{"class":572},[562,1170,1172],{"class":564,"line":1171},14,[562,1173,1174],{"class":572},"}\n",[526,1176,1177],{},"And the registration:",[552,1179,1182],{"className":554,"code":1180,"filename":1181,"language":557,"meta":558,"style":558},"import { createResolver, defineNuxtModule } from '@nuxt\u002Fkit'\nimport { createModuleImport } from '@websideproject\u002Fnuxt-auto-api'\n\nexport default defineNuxtModule({\n  meta: { name: 'blog' },\n  setup(_options, nuxt) {\n    const resolver = createResolver(import.meta.url)\n\n    nuxt.hook('autoApi:registerSchema', (registry) => {\n      registry.register('posts', {\n        schema: createModuleImport(resolver.resolve('..\u002F..\u002Fserver\u002Fdatabase\u002Fschema'), 'posts'),\n        authorization: createModuleImport(resolver.resolve('.\u002Fauth'), 'postsAuth'),\n      })\n    })\n  },\n})\n","modules\u002Fblog\u002Findex.ts",[536,1183,1184,1209,1228,1232,1246,1270,1289,1317,1321,1353,1375,1417,1458,1465,1472,1477],{"__ignoreMap":558},[562,1185,1186,1188,1190,1193,1195,1198,1200,1202,1204,1207],{"class":564,"line":565},[562,1187,569],{"class":568},[562,1189,573],{"class":572},[562,1191,1192],{"class":576}," createResolver",[562,1194,580],{"class":572},[562,1196,1197],{"class":576}," defineNuxtModule",[562,1199,591],{"class":572},[562,1201,594],{"class":568},[562,1203,597],{"class":572},[562,1205,1206],{"class":600},"@nuxt\u002Fkit",[562,1208,604],{"class":572},[562,1210,1211,1213,1215,1218,1220,1222,1224,1226],{"class":564,"line":607},[562,1212,569],{"class":568},[562,1214,573],{"class":572},[562,1216,1217],{"class":576}," createModuleImport",[562,1219,591],{"class":572},[562,1221,594],{"class":568},[562,1223,597],{"class":572},[562,1225,538],{"class":600},[562,1227,604],{"class":572},[562,1229,1230],{"class":564,"line":614},[562,1231,611],{"emptyLinePlaceholder":610},[562,1233,1234,1236,1239,1241,1243],{"class":564,"line":649},[562,1235,617],{"class":568},[562,1237,1238],{"class":568}," default",[562,1240,1197],{"class":630},[562,1242,633],{"class":576},[562,1244,1245],{"class":572},"{\n",[562,1247,1248,1251,1253,1255,1258,1260,1262,1265,1267],{"class":564,"line":700},[562,1249,1250],{"class":652},"  meta",[562,1252,656],{"class":572},[562,1254,573],{"class":572},[562,1256,1257],{"class":652}," name",[562,1259,656],{"class":572},[562,1261,597],{"class":572},[562,1263,1264],{"class":600},"blog",[562,1266,636],{"class":572},[562,1268,1269],{"class":572}," },\n",[562,1271,1272,1275,1277,1280,1282,1285,1287],{"class":564,"line":731},[562,1273,1274],{"class":652},"  setup",[562,1276,633],{"class":572},[562,1278,1279],{"class":1105},"_options",[562,1281,580],{"class":572},[562,1283,1284],{"class":1105}," nuxt",[562,1286,670],{"class":572},[562,1288,646],{"class":572},[562,1290,1291,1294,1297,1299,1301,1303,1305,1307,1310,1312,1315],{"class":564,"line":767},[562,1292,1293],{"class":620},"    const",[562,1295,1296],{"class":576}," resolver",[562,1298,1005],{"class":572},[562,1300,1192],{"class":630},[562,1302,633],{"class":652},[562,1304,569],{"class":568},[562,1306,673],{"class":572},[562,1308,1309],{"class":576},"meta",[562,1311,673],{"class":572},[562,1313,1314],{"class":576},"url",[562,1316,933],{"class":652},[562,1318,1319],{"class":564,"line":790},[562,1320,611],{"emptyLinePlaceholder":610},[562,1322,1323,1326,1328,1331,1333,1335,1338,1340,1342,1344,1347,1349,1351],{"class":564,"line":843},[562,1324,1325],{"class":576},"    nuxt",[562,1327,673],{"class":572},[562,1329,1330],{"class":630},"hook",[562,1332,633],{"class":652},[562,1334,636],{"class":572},[562,1336,1337],{"class":600},"autoApi:registerSchema",[562,1339,636],{"class":572},[562,1341,580],{"class":572},[562,1343,1102],{"class":572},[562,1345,1346],{"class":1105},"registry",[562,1348,670],{"class":572},[562,1350,913],{"class":620},[562,1352,646],{"class":572},[562,1354,1355,1358,1360,1363,1365,1367,1369,1371,1373],{"class":564,"line":866},[562,1356,1357],{"class":576},"      registry",[562,1359,673],{"class":572},[562,1361,1362],{"class":630},"register",[562,1364,633],{"class":652},[562,1366,636],{"class":572},[562,1368,639],{"class":600},[562,1370,636],{"class":572},[562,1372,580],{"class":572},[562,1374,646],{"class":572},[562,1376,1377,1380,1382,1384,1386,1389,1391,1394,1396,1398,1401,1403,1405,1407,1409,1411,1413,1415],{"class":564,"line":927},[562,1378,1379],{"class":652},"        schema",[562,1381,656],{"class":572},[562,1383,1217],{"class":630},[562,1385,633],{"class":652},[562,1387,1388],{"class":576},"resolver",[562,1390,673],{"class":572},[562,1392,1393],{"class":630},"resolve",[562,1395,633],{"class":652},[562,1397,636],{"class":572},[562,1399,1400],{"class":600},"..\u002F..\u002Fserver\u002Fdatabase\u002Fschema",[562,1402,636],{"class":572},[562,1404,670],{"class":652},[562,1406,580],{"class":572},[562,1408,597],{"class":572},[562,1410,639],{"class":600},[562,1412,636],{"class":572},[562,1414,670],{"class":652},[562,1416,697],{"class":572},[562,1418,1419,1422,1424,1426,1428,1430,1432,1434,1436,1438,1441,1443,1445,1447,1449,1452,1454,1456],{"class":564,"line":1094},[562,1420,1421],{"class":652},"        authorization",[562,1423,656],{"class":572},[562,1425,1217],{"class":630},[562,1427,633],{"class":652},[562,1429,1388],{"class":576},[562,1431,673],{"class":572},[562,1433,1393],{"class":630},[562,1435,633],{"class":652},[562,1437,636],{"class":572},[562,1439,1440],{"class":600},".\u002Fauth",[562,1442,636],{"class":572},[562,1444,670],{"class":652},[562,1446,580],{"class":572},[562,1448,597],{"class":572},[562,1450,1451],{"class":600},"postsAuth",[562,1453,636],{"class":572},[562,1455,670],{"class":652},[562,1457,697],{"class":572},[562,1459,1460,1463],{"class":564,"line":1118},[562,1461,1462],{"class":572},"      }",[562,1464,933],{"class":652},[562,1466,1467,1470],{"class":564,"line":1171},[562,1468,1469],{"class":572},"    }",[562,1471,933],{"class":652},[562,1473,1475],{"class":564,"line":1474},15,[562,1476,1086],{"class":572},[562,1478,1480,1482],{"class":564,"line":1479},16,[562,1481,930],{"class":572},[562,1483,933],{"class":576},[526,1485,1486],{},"You get these routes:",[552,1488,1492],{"className":1489,"code":1490,"language":1491,"meta":558,"style":558},"language-http shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","GET    \u002Fapi\u002Fposts        # filters, sorting, pagination, ?include= relations\nGET    \u002Fapi\u002Fposts\u002F:id\nPOST   \u002Fapi\u002Fposts        # validated from the schema\nPATCH  \u002Fapi\u002Fposts\u002F:id\nDELETE \u002Fapi\u002Fposts\u002F:id\n","http",[536,1493,1494,1499,1504,1509,1514],{"__ignoreMap":558},[562,1495,1496],{"class":564,"line":565},[562,1497,1498],{},"GET    \u002Fapi\u002Fposts        # filters, sorting, pagination, ?include= relations\n",[562,1500,1501],{"class":564,"line":607},[562,1502,1503],{},"GET    \u002Fapi\u002Fposts\u002F:id\n",[562,1505,1506],{"class":564,"line":614},[562,1507,1508],{},"POST   \u002Fapi\u002Fposts        # validated from the schema\n",[562,1510,1511],{"class":564,"line":649},[562,1512,1513],{},"PATCH  \u002Fapi\u002Fposts\u002F:id\n",[562,1515,1516],{"class":564,"line":700},[562,1517,1518],{},"DELETE \u002Fapi\u002Fposts\u002F:id\n",[521,1520,1522],{"id":1521},"denied-unless-you-say-otherwise","Denied unless you say otherwise",[526,1524,1525,1526,1529,1530,1533],{},"This was the main design rule. A resource registered without ",[536,1527,1528],{},"authorization"," refuses every request. Permissions go per operation, per row and per field. If a caller can't see a row, it's a 404 on every route that could reach it: get, update, delete, bulk, aggregates, ",[536,1531,1532],{},"?include=",". Not only in the list.",[526,1535,1536],{},"The endpoints I still write by hand go through the same permission check, and load rows with helpers that apply the same row rules. So the app has one permission system instead of one per handler.",[521,1538,1540],{"id":1539},"on-the-client","On the client",[526,1542,1543],{},"TanStack Query composables, auto-imported, typed with your row type:",[552,1545,1550],{"className":1546,"code":1547,"filename":1548,"language":1549,"meta":558,"style":558},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript setup lang=\"ts\">\nimport type { posts } from '~~\u002Fserver\u002Fdatabase\u002Fschema'\n\ntype Post = typeof posts.$inferSelect \u002F\u002F a type-only import: no server code reaches the client\n\nconst { data, isLoading } = useAutoApiList\u003CPost>('posts', {\n  sort: '-createdAt',\n  limit: 10,\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp v-if=\"isLoading\">Loading…\u003C\u002Fp>\n  \u003Carticle v-for=\"post in data?.data\" v-else :key=\"post.id\">{{ post.title }}\u003C\u002Farticle>\n\u003C\u002Ftemplate>\n","app\u002Fpages\u002Fblog.vue","vue",[536,1551,1552,1578,1600,1604,1627,1631,1673,1689,1702,1708,1717,1721,1730,1760,1805],{"__ignoreMap":558},[562,1553,1554,1557,1560,1563,1566,1568,1571,1573,1575],{"class":564,"line":565},[562,1555,1556],{"class":572},"\u003C",[562,1558,1559],{"class":652},"script",[562,1561,1562],{"class":620}," setup",[562,1564,1565],{"class":620}," lang",[562,1567,627],{"class":572},[562,1569,1570],{"class":572},"\"",[562,1572,557],{"class":600},[562,1574,1570],{"class":572},[562,1576,1577],{"class":572},">\n",[562,1579,1580,1582,1584,1586,1589,1591,1593,1595,1598],{"class":564,"line":607},[562,1581,569],{"class":568},[562,1583,949],{"class":568},[562,1585,573],{"class":572},[562,1587,1588],{"class":576}," posts",[562,1590,591],{"class":572},[562,1592,594],{"class":568},[562,1594,597],{"class":572},[562,1596,1597],{"class":600},"~~\u002Fserver\u002Fdatabase\u002Fschema",[562,1599,604],{"class":572},[562,1601,1602],{"class":564,"line":614},[562,1603,611],{"emptyLinePlaceholder":610},[562,1605,1606,1609,1612,1614,1617,1619,1621,1624],{"class":564,"line":649},[562,1607,1608],{"class":620},"type",[562,1610,1611],{"class":1002}," Post",[562,1613,1005],{"class":572},[562,1615,1616],{"class":572}," typeof",[562,1618,1588],{"class":576},[562,1620,673],{"class":572},[562,1622,1623],{"class":576},"$inferSelect ",[562,1625,1626],{"class":1030},"\u002F\u002F a type-only import: no server code reaches the client\n",[562,1628,1629],{"class":564,"line":700},[562,1630,611],{"emptyLinePlaceholder":610},[562,1632,1633,1636,1638,1641,1643,1646,1648,1650,1653,1655,1658,1661,1663,1665,1667,1669,1671],{"class":564,"line":731},[562,1634,1635],{"class":620},"const",[562,1637,573],{"class":572},[562,1639,1640],{"class":576}," data",[562,1642,580],{"class":572},[562,1644,1645],{"class":576}," isLoading ",[562,1647,930],{"class":572},[562,1649,1005],{"class":572},[562,1651,1652],{"class":630}," useAutoApiList",[562,1654,1556],{"class":572},[562,1656,1657],{"class":1002},"Post",[562,1659,1660],{"class":572},">",[562,1662,633],{"class":576},[562,1664,636],{"class":572},[562,1666,639],{"class":600},[562,1668,636],{"class":572},[562,1670,580],{"class":572},[562,1672,646],{"class":572},[562,1674,1675,1678,1680,1682,1685,1687],{"class":564,"line":767},[562,1676,1677],{"class":652},"  sort",[562,1679,656],{"class":572},[562,1681,597],{"class":572},[562,1683,1684],{"class":600},"-createdAt",[562,1686,636],{"class":572},[562,1688,697],{"class":572},[562,1690,1691,1694,1696,1700],{"class":564,"line":790},[562,1692,1693],{"class":652},"  limit",[562,1695,656],{"class":572},[562,1697,1699],{"class":1698},"sbssI"," 10",[562,1701,697],{"class":572},[562,1703,1704,1706],{"class":564,"line":843},[562,1705,930],{"class":572},[562,1707,933],{"class":576},[562,1709,1710,1713,1715],{"class":564,"line":866},[562,1711,1712],{"class":572},"\u003C\u002F",[562,1714,1559],{"class":652},[562,1716,1577],{"class":572},[562,1718,1719],{"class":564,"line":927},[562,1720,611],{"emptyLinePlaceholder":610},[562,1722,1723,1725,1728],{"class":564,"line":1094},[562,1724,1556],{"class":572},[562,1726,1727],{"class":652},"template",[562,1729,1577],{"class":572},[562,1731,1732,1735,1737,1740,1742,1744,1747,1749,1751,1754,1756,1758],{"class":564,"line":1118},[562,1733,1734],{"class":572},"  \u003C",[562,1736,526],{"class":652},[562,1738,1739],{"class":620}," v-if",[562,1741,627],{"class":572},[562,1743,1570],{"class":572},[562,1745,1746],{"class":600},"isLoading",[562,1748,1570],{"class":572},[562,1750,1660],{"class":572},[562,1752,1753],{"class":576},"Loading…",[562,1755,1712],{"class":572},[562,1757,526],{"class":652},[562,1759,1577],{"class":572},[562,1761,1762,1764,1767,1770,1772,1774,1777,1779,1782,1785,1787,1789,1792,1794,1796,1799,1801,1803],{"class":564,"line":1171},[562,1763,1734],{"class":572},[562,1765,1766],{"class":652},"article",[562,1768,1769],{"class":620}," v-for",[562,1771,627],{"class":572},[562,1773,1570],{"class":572},[562,1775,1776],{"class":600},"post in data?.data",[562,1778,1570],{"class":572},[562,1780,1781],{"class":620}," v-else",[562,1783,1784],{"class":620}," :key",[562,1786,627],{"class":572},[562,1788,1570],{"class":572},[562,1790,1791],{"class":600},"post.id",[562,1793,1570],{"class":572},[562,1795,1660],{"class":572},[562,1797,1798],{"class":576},"{{ post.title }}",[562,1800,1712],{"class":572},[562,1802,1766],{"class":652},[562,1804,1577],{"class":572},[562,1806,1807,1809,1811],{"class":564,"line":1474},[562,1808,1712],{"class":572},[562,1810,1727],{"class":652},[562,1812,1577],{"class":572},[526,1814,1815],{},[1816,1817],"img",{"alt":1818,"src":1819},"The playground's posts page built on useAutoApiList: filter, sort and search controls above a list of posts","\u002Fblog\u002Fposts\u002F202610\u002Fnuxt-auto-api-launch\u002Fposts-list.webp",[521,1821,1823],{"id":1822},"what-else-is-in-010","What else is in 0.1.0",[1825,1826,1827,1831,1834,1840,1843],"ul",{},[1828,1829,1830],"li",{},"Multi-tenancy. The tenant always comes from the server, never from a header, and requests without one are refused",[1828,1832,1833],{},"Scoped API tokens, lifecycle hooks, aggregations, many-to-many, soft delete with restore",[1828,1835,1836,1839],{},[536,1837,1838],{},"createEndpoint"," for custom endpoints: the same permission check as the generated routes, plus helpers that load rows with the same row rules",[1828,1841,1842],{},"The same test suite running on SQLite, D1, Turso, Postgres, MySQL and PlanetScale",[1828,1844,1845],{},"An optional admin panel built from the same resources and the same permission rules",[521,1847,1849],{"id":1848},"compared-to-what-i-used-before","Compared to what I used before",[526,1851,1852],{},"I used Django for years, and that's where the \"less boilerplate\" itch comes from. But nuxt-auto isn't a Django port, and in a few places I think it goes further:",[1825,1854,1855,1862,1865,1868],{},[1828,1856,1857,1858,1861],{},"Django REST Framework allows every request by default (",[536,1859,1860],{},"AllowAny",") until you add permission classes. Here nothing is allowed until you declare it.",[1828,1863,1864],{},"Row and field rules are part of the resource config, not something every view has to add.",[1828,1866,1867],{},"The API and the admin use the same rules. In Django the admin has its own permission system next to DRF's.",[1828,1869,1870],{},"Types come from the Drizzle schema: the row type Drizzle infers is what you pass to the composables.",[526,1872,1873],{},"In my own apps I don't write about 60-70% of the endpoints anymore, and the rest use the same permission rules. That's from my projects, yours will be different, but it's the reason this exists.",[526,1875,1876],{},"I'll write about the parts separately over the next two weeks: the permission layers, custom endpoints, the composables, multi-tenancy, D1 and the admin.",[521,1878,1880],{"id":1879},"try-it","Try it",[552,1882,1886],{"className":1883,"code":1884,"language":1885,"meta":558,"style":558},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npx nuxt module add @websideproject\u002Fnuxt-auto-api\n","bash",[536,1887,1888],{"__ignoreMap":558},[562,1889,1890,1893,1895,1898,1901],{"class":564,"line":565},[562,1891,1892],{"class":1002},"npx",[562,1894,1284],{"class":600},[562,1896,1897],{"class":600}," module",[562,1899,1900],{"class":600}," add",[562,1902,1903],{"class":600}," @websideproject\u002Fnuxt-auto-api\n",[526,1905,1906],{},"Or run the playground. It has around a dozen API demos (roles, row rules, field rules, tokens, bulk operations) with a demo user per role, and the admin:",[552,1908,1910],{"className":1883,"code":1909,"language":1885,"meta":558,"style":558},"git clone https:\u002F\u002Fgithub.com\u002Fwebsideproject\u002Fnuxt-auto\ncd nuxt-auto\nbun install\nbun run dev:prepare\n\ncd apps\u002Fplayground\nmkdir -p .data\nbun run db:migrate && bun run db:seed   # creates and fills the demo database\ncd ..\u002F..\n\nbun run dev\n",[536,1911,1912,1923,1931,1939,1949,1953,1960,1971,1994,2001,2005],{"__ignoreMap":558},[562,1913,1914,1917,1920],{"class":564,"line":565},[562,1915,1916],{"class":1002},"git",[562,1918,1919],{"class":600}," clone",[562,1921,1922],{"class":600}," https:\u002F\u002Fgithub.com\u002Fwebsideproject\u002Fnuxt-auto\n",[562,1924,1925,1928],{"class":564,"line":607},[562,1926,1927],{"class":630},"cd",[562,1929,1930],{"class":600}," nuxt-auto\n",[562,1932,1933,1936],{"class":564,"line":614},[562,1934,1935],{"class":1002},"bun",[562,1937,1938],{"class":600}," install\n",[562,1940,1941,1943,1946],{"class":564,"line":649},[562,1942,1935],{"class":1002},[562,1944,1945],{"class":600}," run",[562,1947,1948],{"class":600}," dev:prepare\n",[562,1950,1951],{"class":564,"line":700},[562,1952,611],{"emptyLinePlaceholder":610},[562,1954,1955,1957],{"class":564,"line":731},[562,1956,1927],{"class":630},[562,1958,1959],{"class":600}," apps\u002Fplayground\n",[562,1961,1962,1965,1968],{"class":564,"line":767},[562,1963,1964],{"class":1002},"mkdir",[562,1966,1967],{"class":600}," -p",[562,1969,1970],{"class":600}," .data\n",[562,1972,1973,1975,1977,1980,1983,1986,1988,1991],{"class":564,"line":790},[562,1974,1935],{"class":1002},[562,1976,1945],{"class":600},[562,1978,1979],{"class":600}," db:migrate",[562,1981,1982],{"class":572}," &&",[562,1984,1985],{"class":1002}," bun",[562,1987,1945],{"class":600},[562,1989,1990],{"class":600}," db:seed",[562,1992,1993],{"class":1030},"   # creates and fills the demo database\n",[562,1995,1996,1998],{"class":564,"line":843},[562,1997,1927],{"class":630},[562,1999,2000],{"class":600}," ..\u002F..\n",[562,2002,2003],{"class":564,"line":866},[562,2004,611],{"emptyLinePlaceholder":610},[562,2006,2007,2009,2011],{"class":564,"line":927},[562,2008,1935],{"class":1002},[562,2010,1945],{"class":600},[562,2012,2013],{"class":600}," dev\n",[1825,2015,2016,2023],{},[1828,2017,2018,2019],{},"Docs: ",[2020,2021,2022],"a",{"href":182},"websideproject.com\u002Fdocs\u002Fnuxt-auto",[1828,2024,2025,2026],{},"Repo: ",[2020,2027,2031],{"href":2028,"rel":2029},"https:\u002F\u002Fgithub.com\u002Fwebsideproject\u002Fnuxt-auto",[2030],"nofollow","github.com\u002Fwebsideproject\u002Fnuxt-auto",[526,2033,2034],{},"MIT licensed. If you try it, I'd like to hear what you think of the permission API, and what you'd want from the admin.",[2036,2037,2038],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":558,"searchDepth":607,"depth":607,"links":2040},[2041,2042,2043,2044,2045,2046,2047],{"id":523,"depth":607,"text":524},{"id":546,"depth":607,"text":547},{"id":1521,"depth":607,"text":1522},{"id":1539,"depth":607,"text":1540},{"id":1822,"depth":607,"text":1823},{"id":1848,"depth":607,"text":1849},{"id":1879,"depth":607,"text":1880},"2026-10-12","nuxt-auto-api 0.1.0 is out. Register a Drizzle table and you get a REST API with filters, relations and permissions that deny by default. Less backend boilerplate in Nuxt, and why I built it.","md",{"src":2052},"blog\u002Fposts\u002F202610\u002Fnuxt-auto-api-launch\u002Fcover.webp",{},null,"\u002Fblog\u002Fnuxt-auto-api-launch",{"title":509,"description":2049},"3.blog\u002F10.nuxt-auto-api-launch",[2059,2060,2061,2062],"Nuxt","nuxt-auto","Drizzle","Open Source","b3LMkVSlREyFietPw4OfzvvarQYZ5dOxa3RlmKMUbyw",[2065,2070],{"title":2066,"path":2067,"stem":2068,"description":2069,"children":-1},"Self-Hosting Nuxt on Cloudflare Workers: Setup & Migration Guide","\u002Fblog\u002Fmigrate-nuxthub-to-wrangler","3.blog\u002F1.migrate-nuxthub-to-wrangler","How to deploy Nuxt directly to Cloudflare Workers with wrangler.jsonc. Includes automated scripts for setup, migrations, and deployment. Works for new projects or migrating from NuxtHub.",{"title":2071,"path":2072,"stem":2073,"description":2074,"children":-1},"nuxt-protokit: rapid prototyping for Nuxt","\u002Fblog\u002Fnuxt-protokit-launch","3.blog\u002F11.nuxt-protokit-launch","Prototype a tool in one TypeScript schema and it runs in Nuxt UI, saved in the browser as you type. No backend, no database. What it is and where I use it.",1791895766357]