prisma/schema.prisma no genera el cliente en su ubicación habitual: apunta
a src/generated/prisma, una carpeta que está gitignored. Cada persona
que clona el repo (y cada build) tiene que correr npx prisma generate para
que esa carpeta exista.Como consecuencia, el código nunca importa desde @prisma/client. Importa
siempre desde el cliente generado:
import type { Prisma } from "@/generated/prisma/client";import { Role } from "@/generated/prisma/enums";
El bloque datasource de schema.prisma no lleva url. La conexión a la
base de datos viene de prisma.config.ts, que carga dotenv/config para
leer las variables de entorno.src/lib/prisma.ts tampoco pasa la URL directamente al cliente: la parsea a
mano (new URL(DATABASE_URL)) y con sus piezas (host, usuario, contraseña,
base de datos, puerto) construye el adapter PrismaMariaDb.
PRISMA_SCHEMA_VERSION, la constante definida en src/lib/prisma.ts, hay
que incrementarla cada vez que se añade o se quita un modelo del schema. Si
no se incrementa, el HMR de next dev puede reutilizar un client viejo en
memoria y las consultas dejan de coincidir con el schema real.
User tiene city y neighborhood (opcionales): es dónde vive o busca
la persona. Se piden en el registro por email, se editan en el perfil y
precargan el dónde de la búsqueda.Camello tiene su propia city y neighborhood: es su zona de servicio,
la que filtra GET /api/camellos. Son datos distintos y pueden diferir.
Editar la ubicación en el perfil solo escribe en User. Al registrar un
camello, la ciudad y el barrio se copian también al User, y la migración
copió esos valores a los usuarios con camello vinculado que no tenían ciudad.
Los helpers que consultan la base de datos llaman await connection() (de
next/server) antes de ejecutar la query. Esto impide que Next.js intente
prerenderizar esas páginas en build time: el build de Docker corre con una
DATABASE_URL falsa, así que cualquier consulta real durante el build
fallaría.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"CamellApp","colors":{"primary":"#c88d2f","light":"#e0b46a","dark":"#122c51"},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"camellapp","hasAudioNarration":false,"actualSubdomain":"camellapp","customDomains":["docs.camellapp.com"],"gitSource":{"type":"github","owner":"mangomorado","repo":"camellapp","deployBranch":"master","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6aec9a1f8c2847cc731fd8b8","clientVersion":"0.0.4076","searchFilterCounts":[{"language":"default","tag":"API","count":65},{"language":"default","tag":"Dominio","count":9},{"language":"default","tag":"Notifications","count":8},{"language":"default","tag":"Jobs","count":7},{"language":"default","tag":"Admin Users","count":6},{"language":"default","tag":"Agenda","count":6},{"language":"default","tag":"Me","count":6},{"language":"default","tag":"Arquitectura","count":5},{"language":"default","tag":"Admin Camellos","count":5},{"language":"default","tag":"Auth","count":5}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"docs/handbook/arquitectura/datos-y-prisma","slug":"/docs/handbook/arquitectura/datos-y-prisma","pageMetadata":{"title":"Datos y Prisma","description":"El cliente generado fuera de node_modules y por qué importa.","href":"/docs/handbook/arquitectura/datos-y-prisma"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"El cliente generado fuera de node_modules y por qué importa.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={code:`code`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,..._provideComponents(),...props.components},{CodeBlock,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(Heading,{level:`2`,id:`cliente-generado-fuera-de-node_modules`,children:[`Cliente generado fuera de `,_jsx(_components.code,{children:`node_modules`})]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`prisma/schema.prisma`}),` no genera el cliente en su ubicación habitual: apunta\na `,_jsx(_components.code,{children:`src/generated/prisma`}),`, una carpeta que está `,_jsx(_components.strong,{children:`gitignored`}),`. Cada persona\nque clona el repo (y cada build) tiene que correr `,_jsx(_components.code,{children:`npx prisma generate`}),` para\nque esa carpeta exista.`]}),`\n`,_jsxs(_components.p,{children:[`Como consecuencia, el código `,_jsx(_components.strong,{children:`nunca`}),` importa desde `,_jsx(_components.code,{children:`@prisma/client`}),`. Importa\nsiempre desde el cliente generado:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`typescript`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`typescript`,children:_jsxs(_components.code,{language:`typescript`,numberOfLines:`2`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:` type`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` { `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`Prisma`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` } `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`from`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"@/generated/prisma/client\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`import`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` { `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#9CDCFE`},children:`Role`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` } `}),_jsx(_components.span,{style:{color:`#CF222E`,\"--shiki-dark\":`#C586C0`},children:`from`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:` \"@/generated/prisma/enums\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`;`})]}),`\n`]})})}),`\n`,_jsxs(Heading,{level:`2`,id:`conexión-prisma-config-ts-no-la-url-a-pelo`,children:[`Conexión: `,_jsx(_components.code,{children:`prisma.config.ts`}),`, no la URL a pelo`]}),`\n`,_jsxs(_components.p,{children:[`El bloque `,_jsx(_components.code,{children:`datasource`}),` de `,_jsx(_components.code,{children:`schema.prisma`}),` no lleva `,_jsx(_components.code,{children:`url`}),`. La conexión a la\nbase de datos viene de `,_jsx(_components.code,{children:`prisma.config.ts`}),`, que carga `,_jsx(_components.code,{children:`dotenv/config`}),` para\nleer las variables de entorno.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`src/lib/prisma.ts`}),` tampoco pasa la URL directamente al cliente: la parsea a\nmano (`,_jsx(_components.code,{children:`new URL(DATABASE_URL)`}),`) y con sus piezas (host, usuario, contraseña,\nbase de datos, puerto) construye el adapter `,_jsx(_components.code,{children:`PrismaMariaDb`}),`.`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[_jsx(_components.code,{children:`PRISMA_SCHEMA_VERSION`}),`, la constante definida en `,_jsx(_components.code,{children:`src/lib/prisma.ts`}),`, hay\nque incrementarla cada vez que se añade o se quita un modelo del schema. Si\nno se incrementa, el HMR de `,_jsx(_components.code,{children:`next dev`}),` puede reutilizar un client viejo en\nmemoria y las consultas dejan de coincidir con el schema real.`]})}),`\n`,_jsxs(Heading,{level:`2`,id:`ubicación-user-frente-a-camello`,children:[`Ubicación: `,_jsx(_components.code,{children:`User`}),` frente a `,_jsx(_components.code,{children:`Camello`})]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`User`}),` tiene `,_jsx(_components.code,{children:`city`}),` y `,_jsx(_components.code,{children:`neighborhood`}),` (opcionales): es `,_jsx(_components.strong,{children:`dónde vive o busca`}),`\nla persona. Se piden en el registro por email, se editan en el perfil y\nprecargan el dónde de la búsqueda.`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`Camello`}),` tiene su propia `,_jsx(_components.code,{children:`city`}),` y `,_jsx(_components.code,{children:`neighborhood`}),`: es su `,_jsx(_components.strong,{children:`zona de servicio`}),`,\nla que filtra `,_jsx(_components.code,{children:`GET /api/camellos`}),`. Son datos distintos y pueden diferir.\nEditar la ubicación en el perfil solo escribe en `,_jsx(_components.code,{children:`User`}),`. Al registrar un\ncamello, la ciudad y el barrio se copian también al `,_jsx(_components.code,{children:`User`}),`, y la migración\ncopió esos valores a los usuarios con camello vinculado que no tenían ciudad.`]}),`\n`,_jsxs(Heading,{level:`2`,id:`connection-antes-de-consultar`,children:[_jsx(_components.code,{children:`connection()`}),` antes de consultar`]}),`\n`,_jsxs(_components.p,{children:[`Los helpers que consultan la base de datos llaman `,_jsx(_components.code,{children:`await connection()`}),` (de\n`,_jsx(_components.code,{children:`next/server`}),`) antes de ejecutar la query. Esto impide que Next.js intente\nprerenderizar esas páginas en build time: el build de Docker corre con una\n`,_jsx(_components.code,{children:`DATABASE_URL`}),` falsa, así que cualquier consulta real durante el build\nfallaría.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Datos y Prisma","description":"El cliente generado fuera de node_modules y por qué importa.","href":"/docs/handbook/arquitectura/datos-y-prisma"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Cliente generado fuera de node_modules","slug":"cliente-generado-fuera-de-node_modules","depth":2,"children":[]},{"title":"Conexión: prisma.config.ts, no la URL a pelo","slug":"conexión-prisma-config-ts-no-la-url-a-pelo","depth":2,"children":[]},{"title":"Ubicación: User frente a Camello","slug":"ubicación-user-frente-a-camello","depth":2,"children":[]},{"title":"connection() antes de consultar","slug":"connection-antes-de-consultar","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"camellapp","siteName":"CamellApp","title":"Datos y Prisma - CamellApp","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-10-22T16:26:35.195Z","originalFileLocation":"docs/handbook/arquitectura/datos-y-prisma.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"El cliente generado fuera de node_modules y por qué importa.\">\u003cmeta data-mint-head name=\"application-name\" content=\"CamellApp\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/camellapp/_generated/favicon/apple-touch-icon.png?v=3\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/camellapp/_generated/favicon/favicon-32x32.png?v=3\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/camellapp/_generated/favicon/favicon-16x16.png?v=3\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/camellapp/_generated/favicon/favicon.ico?v=3\">\u003cmeta data-mint-head property=\"og:title\" content=\"Datos y Prisma - CamellApp\">\u003cmeta data-mint-head property=\"og:description\" content=\"El cliente generado fuera de node_modules y por qué importa.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"CamellApp\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://camellapp.mintlify.site/_mintlify/api/og/v2?division=Arquitectura\u0026amp;title=Datos+y+Prisma\u0026amp;description=El+cliente+generado+fuera+de+node_modules+y+por+qu%C3%A9+importa.\u0026amp;theme=e6c2b81cb548feb29b388616\u0026amp;signature=3a4f362642cc73883aaf25224e249da6ee23dc0248d6a98eae7002e51f8c702c\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Datos y Prisma - CamellApp\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"El cliente generado fuera de node_modules y por qué importa.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://camellapp.mintlify.site/_mintlify/api/og/v2?division=Arquitectura\u0026amp;title=Datos+y+Prisma\u0026amp;description=El+cliente+generado+fuera+de+node_modules+y+por+qu%C3%A9+importa.\u0026amp;theme=e6c2b81cb548feb29b388616\u0026amp;signature=3a4f362642cc73883aaf25224e249da6ee23dc0248d6a98eae7002e51f8c702c\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"https://mintlify.s3.us-west-1.amazonaws.com/camellapp/_generated/favicon/browserconfig.xml?v=3\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"CamellApp\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#c88d2f\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/docs/handbook/arquitectura/datos-y-prisma.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.camellapp.com/#organization\",\"name\":\"CamellApp\",\"url\":\"https://docs.camellapp.com\"},{\"@type\":\"WebSite\",\"@id\":\"https://docs.camellapp.com#website\",\"name\":\"CamellApp\",\"url\":\"https://docs.camellapp.com\",\"publisher\":{\"@id\":\"https://docs.camellapp.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma#webpage\",\"url\":\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma\",\"name\":\"Datos y Prisma\",\"description\":\"El cliente generado fuera de node_modules y por qué importa.\",\"dateModified\":\"2026-10-22T16:26:35.195Z\",\"isPartOf\":{\"@id\":\"https://docs.camellapp.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Arquitectura\",\"item\":\"https://docs.camellapp.com/docs/handbook/arquitectura/vision-general\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Datos y Prisma\",\"item\":\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma#article\",\"headline\":\"Datos y Prisma\",\"name\":\"Datos y Prisma\",\"description\":\"El cliente generado fuera de node_modules y por qué importa.\",\"url\":\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma\",\"mainEntityOfPage\":{\"@id\":\"https://docs.camellapp.com/docs/handbook/arquitectura/datos-y-prisma#webpage\"},\"dateModified\":\"2026-10-22T16:26:35.195Z\",\"publisher\":{\"@id\":\"https://docs.camellapp.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.camellapp.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"CamellApp\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"CamellApp","nav":{"global":null,"groups":[{"group":"Inicio","pages":[{"title":"CamellApp","href":"/index"}]},{"group":"Novedades","pages":[{"title":"Novedades","href":"/docs/handbook/novedades"}]},{"group":"Empezar","pages":[{"title":"Instalación","href":"/docs/handbook/empezar/instalacion"},{"title":"Variables de entorno","href":"/docs/handbook/empezar/variables-de-entorno"}]},{"group":"Arquitectura","pages":[{"title":"Visión general","description":"El stack de CamellApp y cómo encajan sus piezas.","href":"/docs/handbook/arquitectura/vision-general"},{"title":"Datos y Prisma","href":"/docs/handbook/arquitectura/datos-y-prisma"},{"title":"Estructura del código","description":"src/app frente a src/views, y el split server-only / -shared.","href":"/docs/handbook/arquitectura/estructura-del-codigo"},{"title":"Route handlers","href":"/docs/handbook/arquitectura/route-handlers"},{"title":"Autenticación y roles","href":"/docs/handbook/arquitectura/auth-y-roles"}]},{"group":"Dominio","pages":[{"title":"Trabajos (Jobs)","href":"/docs/handbook/dominio/jobs"},{"title":"Agenda","href":"/docs/handbook/dominio/agenda"},{"title":"Matching por score","href":"/docs/handbook/dominio/matching"},{"title":"Búsqueda conversacional","href":"/docs/handbook/dominio/busqueda"},{"title":"Precio referencial","href":"/docs/handbook/dominio/precio-referencial"},{"title":"Notificaciones","href":"/docs/handbook/dominio/notificaciones"},{"title":"Auditoría de acceso","href":"/docs/handbook/dominio/auditoria"},{"title":"Textos legales","href":"/docs/handbook/dominio/legales"},{"title":"Centro de ayuda","href":"/docs/handbook/dominio/ayuda"}]},{"group":"Guías","pages":[{"title":"Testing","href":"/docs/handbook/guias/testing"},{"title":"Añadir un endpoint","href":"/docs/handbook/guias/anadir-un-endpoint"},{"title":"Despliegue","href":"/docs/handbook/guias/despliegue"}]},{"group":"Contribuir","pages":[{"title":"Flujo de trabajo y regla de docs","href":"/docs/handbook/contribuir/flujo-y-regla-de-docs"}]},{"group":"API","pages":[{"group":"Auth","pages":[{"title":"Registrar usuario con email y password","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/register","href":"/api-reference/auth/registrar-usuario-con-email-y-password"},{"title":"Registrar prestador (usuario CAMELLOS + perfil Camello)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/register/camello","href":"/api-reference/auth/registrar-prestador-usuario-camellos-+-perfil-camello"},{"title":"Pantalla/flujo de inicio de sesion NextAuth","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/auth/signin","href":"/api-reference/auth/pantallaflujo-de-inicio-de-sesion-nextauth"},{"title":"Obtener sesion actual","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/auth/session","href":"/api-reference/auth/obtener-sesion-actual"},{"title":"Cerrar sesion","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/auth/signout","href":"/api-reference/auth/cerrar-sesion"}]},{"group":"Me","pages":[{"title":"Obtener perfil del usuario autenticado","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/me","href":"/api-reference/me/obtener-perfil-del-usuario-autenticado"},{"title":"Eliminar (anonimizar) la cuenta del usuario autenticado","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml delete /api/me","href":"/api-reference/me/eliminar-anonimizar-la-cuenta-del-usuario-autenticado"},{"title":"Actualizar nombre, telefono, ciudad y/o barrio","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/me","href":"/api-reference/me/actualizar-nombre-telefono-ciudad-yo-barrio"},{"title":"Subir avatar (JPEG/PNG/WebP, max 2 MB)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/me/avatar","href":"/api-reference/me/subir-avatar-jpegpngwebp-max-2-mb"},{"title":"Cambiar contraseña","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/me/password","href":"/api-reference/me/cambiar-contraseña"},{"title":"Actualizar el precio referencial del camello autenticado","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/me/price","href":"/api-reference/me/actualizar-el-precio-referencial-del-camello-autenticado"}]},{"group":"Agenda","pages":[{"title":"Franjas semanales del camello autenticado","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/me/availability","href":"/api-reference/agenda/franjas-semanales-del-camello-autenticado"},{"title":"Reemplaza todas las franjas del camello","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml put /api/me/availability","href":"/api-reference/agenda/reemplaza-todas-las-franjas-del-camello"},{"title":"Huecos libres de 60 minutos de un camello activo","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/camellos/{id}/slots","href":"/api-reference/agenda/huecos-libres-de-60-minutos-de-un-camello-activo"},{"title":"Citas del rango, con la visibilidad de los trabajos","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/agenda","href":"/api-reference/agenda/citas-del-rango-con-la-visibilidad-de-los-trabajos"},{"title":"Recordatorio de citas en las proximas 24 horas","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/cron/appointment-reminders","href":"/api-reference/agenda/recordatorio-de-citas-en-las-proximas-24-horas"},{"title":"Fija o mueve la cita de un trabajo propio","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/jobs/{id}/schedule","href":"/api-reference/agenda/fija-o-mueve-la-cita-de-un-trabajo-propio"}]},{"group":"Camellos","pages":[{"title":"Listar camellos activos (publico)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/camellos","href":"/api-reference/camellos/listar-camellos-activos-publico"},{"title":"Metadatos publicos (conteo, oficios, ciudades)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/camellos/meta","href":"/api-reference/camellos/metadatos-publicos-conteo-oficios-ciudades"},{"title":"Crear reseña de un camello (solo CLIENTES)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/camellos/{id}/reviews","href":"/api-reference/camellos/crear-reseña-de-un-camello-solo-clientes"}]},{"group":"Categories","pages":[{"title":"Listar categorias (publico)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/categories","href":"/api-reference/categories/listar-categorias-publico"}]},{"group":"Health","pages":[{"title":"Probe publico de liveness (DB)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/health","href":"/api-reference/health/probe-publico-de-liveness-db"},{"title":"Snapshot de salud del sistema","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/health","href":"/api-reference/health/snapshot-de-salud-del-sistema"}]},{"group":"Search","pages":[{"title":"Interpretar una busqueda en lenguaje natural (publico)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/search/interpret","href":"/api-reference/search/interpretar-una-busqueda-en-lenguaje-natural-publico"},{"title":"Interpretar una foto del problema (publico)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/search/interpret-image","href":"/api-reference/search/interpretar-una-foto-del-problema-publico"}]},{"group":"Admin Users","pages":[{"title":"Listar usuarios (paginado)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/users","href":"/api-reference/admin-users/listar-usuarios-paginado"},{"title":"Crear usuario","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/admin/users","href":"/api-reference/admin-users/crear-usuario"},{"title":"Obtener usuario por ID","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/users/{id}","href":"/api-reference/admin-users/obtener-usuario-por-id"},{"title":"Eliminar usuario","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml delete /api/admin/users/{id}","href":"/api-reference/admin-users/eliminar-usuario"},{"title":"Actualizar usuario","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/admin/users/{id}","href":"/api-reference/admin-users/actualizar-usuario"},{"title":"Actualizar solo el rol de usuario","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/admin/users/{id}/role","href":"/api-reference/admin-users/actualizar-solo-el-rol-de-usuario"}]},{"group":"Admin Services","pages":[{"title":"Listar servicios","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/services","href":"/api-reference/admin-services/listar-servicios"},{"title":"Crear servicio","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/admin/services","href":"/api-reference/admin-services/crear-servicio"},{"title":"Eliminar servicio","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml delete /api/admin/services/{id}","href":"/api-reference/admin-services/eliminar-servicio"},{"title":"Actualizar servicio","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/admin/services/{id}","href":"/api-reference/admin-services/actualizar-servicio"}]},{"group":"Admin Categories","pages":[{"title":"Listar categorias (con conteo de servicios)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/categories","href":"/api-reference/admin-categories/listar-categorias-con-conteo-de-servicios"},{"title":"Crear categoria","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/admin/categories","href":"/api-reference/admin-categories/crear-categoria"},{"title":"Eliminar categoria","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml delete /api/admin/categories/{id}","href":"/api-reference/admin-categories/eliminar-categoria"},{"title":"Actualizar categoria","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/admin/categories/{id}","href":"/api-reference/admin-categories/actualizar-categoria"}]},{"group":"Admin Camellos","pages":[{"title":"Listar camellos (admin)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/camellos","href":"/api-reference/admin-camellos/listar-camellos-admin"},{"title":"Crear camello (JSON o multipart con foto)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/admin/camellos","href":"/api-reference/admin-camellos/crear-camello-json-o-multipart-con-foto"},{"title":"Obtener camello por ID","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/camellos/{id}","href":"/api-reference/admin-camellos/obtener-camello-por-id"},{"title":"Eliminar camello (hard delete)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml delete /api/admin/camellos/{id}","href":"/api-reference/admin-camellos/eliminar-camello-hard-delete"},{"title":"Actualizar camello (JSON o multipart)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/admin/camellos/{id}","href":"/api-reference/admin-camellos/actualizar-camello-json-o-multipart"}]},{"group":"Jobs","pages":[{"title":"Listar trabajos del usuario autenticado","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/jobs","href":"/api-reference/jobs/listar-trabajos-del-usuario-autenticado"},{"title":"Crear solicitud de trabajo","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/jobs","href":"/api-reference/jobs/crear-solicitud-de-trabajo"},{"title":"Detalle de un trabajo visible para el usuario","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/jobs/{id}","href":"/api-reference/jobs/detalle-de-un-trabajo-visible-para-el-usuario"},{"title":"Camello acepta un trabajo pendiente","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/jobs/{id}/accept","href":"/api-reference/jobs/camello-acepta-un-trabajo-pendiente"},{"title":"Camello rechaza un trabajo pendiente","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/jobs/{id}/reject","href":"/api-reference/jobs/camello-rechaza-un-trabajo-pendiente"},{"title":"Marcar trabajo en progreso como completado","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/jobs/{id}/complete","href":"/api-reference/jobs/marcar-trabajo-en-progreso-como-completado"},{"title":"Cancelar un trabajo pendiente o en progreso","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/jobs/{id}/cancel","href":"/api-reference/jobs/cancelar-un-trabajo-pendiente-o-en-progreso"}]},{"group":"Admin Jobs","pages":[{"title":"Asignar un camello a un trabajo pendiente libre","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/admin/jobs/{id}/assign","href":"/api-reference/admin-jobs/asignar-un-camello-a-un-trabajo-pendiente-libre"},{"title":"Ranking de camellos candidatos para un trabajo","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/jobs/{id}/candidates","href":"/api-reference/admin-jobs/ranking-de-camellos-candidatos-para-un-trabajo"}]},{"group":"Notifications","pages":[{"title":"Listar inbox del usuario","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/notifications","href":"/api-reference/notifications/listar-inbox-del-usuario"},{"title":"Marcar notificacion como leida","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/notifications/{id}/read","href":"/api-reference/notifications/marcar-notificacion-como-leida"},{"title":"Marcar todas como leidas","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/notifications/read-all","href":"/api-reference/notifications/marcar-todas-como-leidas"},{"title":"Clave publica VAPID","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/notifications/push/vapid","href":"/api-reference/notifications/clave-publica-vapid"},{"title":"Guardar suscripcion Web Push","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml post /api/notifications/push/subscribe","href":"/api-reference/notifications/guardar-suscripcion-web-push"},{"title":"Eliminar suscripcion Web Push","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml delete /api/notifications/push/subscribe","href":"/api-reference/notifications/eliminar-suscripcion-web-push"},{"title":"Preferencias de canal","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/me/notification-preferences","href":"/api-reference/notifications/preferencias-de-canal"},{"title":"Actualizar preferencia de canal","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml patch /api/me/notification-preferences","href":"/api-reference/notifications/actualizar-preferencia-de-canal"}]},{"group":"Admin Notifications","pages":[{"title":"Listar envios (deliveries)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/notifications","href":"/api-reference/admin-notifications/listar-envios-deliveries"}]},{"group":"Admin Legal","pages":[{"title":"Listar textos legales editables","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/legal-documents","href":"/api-reference/admin-legal/listar-textos-legales-editables"},{"title":"Publicar una version de un texto legal","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml put /api/admin/legal-documents/{slug}","href":"/api-reference/admin-legal/publicar-una-version-de-un-texto-legal"}]},{"group":"Admin Meta","pages":[{"title":"Obtener especificación OpenAPI (YAML)","deprecated":null,"version":null,"openapi":"/docs/openapi-auth.yaml get /api/admin/openapi","href":"/api-reference/admin-meta/obtener-especificación-openapi-yaml"}]}]}]},"basePath":"","title":"Datos y Prisma - CamellApp","buildId":"dpl_8LJbrAiZvr1K98WQNZXCyY5AvT34","deploymentId":"dpl_8LJbrAiZvr1K98WQNZXCyY5AvT34","dynamics":[],"chunks":[]}