[{"data":1,"prerenderedAt":2342},["ShallowReactive",2],{"page-/post/nuxt/nuxt3-full-stack-prisma-sqlite":3,"surrounding-page":2333},{"id":4,"title":5,"author":6,"body":7,"date":2318,"description":2319,"extension":2320,"group":6,"lastmod":2321,"meta":2322,"navigation":880,"path":2323,"rawbody":2324,"seo":2325,"showTitle":6,"stem":2326,"tags":2327,"versions":2329,"__hash__":2332},"content/post/nuxt/Nuxt3-full-stack-prisma-sqlite.md","Nuxt3全栈开发 · 如何使用Prisma+Sqlite",null,{"type":8,"value":9,"toc":2311},"minimark",[10,23,34,42,232,235,242,246,253,331,352,371,385,400,413,426,432,435,441,446,459,477,525,538,543,549,555,598,617,623,638,781,799,809,901,915,1018,1038,1052,1060,1084,1098,1101,1110,1132,1142,1153,1166,1169,1187,1192,1198,1208,1386,1396,1407,1418,1634,1652,1655,1658,1672,1688,1694,1801,1810,2043,2063,2069,2079,2088,2091,2094,2101,2104,2119,2125,2131,2153,2169,2178,2187,2195,2200,2207,2229,2232,2238,2252,2264,2280,2283,2286,2297,2300,2307],[11,12,13,14,18,19,22],"p",{},"想要在 ",[15,16,17],"code",{},"Nuxt3"," 中使用 ",[15,20,21],{},"Sqlite"," 非常简单，但重点是如何管理表结构的变更。",[11,24,25,26,29,30,33],{},"Nuxt3官方提供了配置，可以直接开启数据库，默认就是",[15,27,28],{},"sqlite","，数据库文件会存储在",[15,31,32],{},".data/"," 目录下。",[11,35,36],{},[37,38,39],"a",{"href":39,"rel":40},"https://db0.unjs.io/connectors/sqlite",[41],"nofollow",[43,44,49],"pre",{"className":45,"code":46,"language":47,"meta":48,"style":48},"language-typescript shiki shiki-themes github-light","nitro: {\n    experimental: {\n      database: true\n    },\n    database: {\n      default: {\n        connector: 'sqlite',\n        options: {\n          path: '/blog',\n          name: 'blog.db'\n        }\n      }\n    },\n    devDatabase: {\n      default: {\n        connector: 'sqlite',\n        options: {\n          path: '/Users/your_name/code/abc/databases/blog',\n          name: 'blog.db'\n        }\n      }\n    }\n  },\n","typescript","",[15,50,51,64,72,85,91,99,108,121,127,138,147,153,159,164,172,179,188,193,203,210,215,220,226],{"__ignoreMap":48},[52,53,56,60],"span",{"class":54,"line":55},"line",1,[52,57,59],{"class":58},"s7eDp","nitro",[52,61,63],{"class":62},"sgsFI",": {\n",[52,65,67,70],{"class":54,"line":66},2,[52,68,69],{"class":58},"    experimental",[52,71,63],{"class":62},[52,73,75,78,81],{"class":54,"line":74},3,[52,76,77],{"class":58},"      database",[52,79,80],{"class":62},": ",[52,82,84],{"class":83},"sYu0t","true\n",[52,86,88],{"class":54,"line":87},4,[52,89,90],{"class":62},"    },\n",[52,92,94,97],{"class":54,"line":93},5,[52,95,96],{"class":58},"    database",[52,98,63],{"class":62},[52,100,102,106],{"class":54,"line":101},6,[52,103,105],{"class":104},"sD7c4","      default",[52,107,63],{"class":62},[52,109,111,114,118],{"class":54,"line":110},7,[52,112,113],{"class":62},"        connector: ",[52,115,117],{"class":116},"sYBdl","'sqlite'",[52,119,120],{"class":62},",\n",[52,122,124],{"class":54,"line":123},8,[52,125,126],{"class":62},"        options: {\n",[52,128,130,133,136],{"class":54,"line":129},9,[52,131,132],{"class":62},"          path: ",[52,134,135],{"class":116},"'/blog'",[52,137,120],{"class":62},[52,139,141,144],{"class":54,"line":140},10,[52,142,143],{"class":62},"          name: ",[52,145,146],{"class":116},"'blog.db'\n",[52,148,150],{"class":54,"line":149},11,[52,151,152],{"class":62},"        }\n",[52,154,156],{"class":54,"line":155},12,[52,157,158],{"class":62},"      }\n",[52,160,162],{"class":54,"line":161},13,[52,163,90],{"class":62},[52,165,167,170],{"class":54,"line":166},14,[52,168,169],{"class":58},"    devDatabase",[52,171,63],{"class":62},[52,173,175,177],{"class":54,"line":174},15,[52,176,105],{"class":104},[52,178,63],{"class":62},[52,180,182,184,186],{"class":54,"line":181},16,[52,183,113],{"class":62},[52,185,117],{"class":116},[52,187,120],{"class":62},[52,189,191],{"class":54,"line":190},17,[52,192,126],{"class":62},[52,194,196,198,201],{"class":54,"line":195},18,[52,197,132],{"class":62},[52,199,200],{"class":116},"'/Users/your_name/code/abc/databases/blog'",[52,202,120],{"class":62},[52,204,206,208],{"class":54,"line":205},19,[52,207,143],{"class":62},[52,209,146],{"class":116},[52,211,213],{"class":54,"line":212},20,[52,214,152],{"class":62},[52,216,218],{"class":54,"line":217},21,[52,219,158],{"class":62},[52,221,223],{"class":54,"line":222},22,[52,224,225],{"class":62},"    }\n",[52,227,229],{"class":54,"line":228},23,[52,230,231],{"class":62},"  },\n",[11,233,234],{},"但是后续的表结构的更新、新增、删除等操作如何和线上同步就是一个大问题了。",[11,236,237,238,241],{},"所以对于单机部署的博客来说，还是用上",[15,239,240],{},"Prisma","省心。",[243,244,245],"h2",{"id":245},"安装及配置",[11,247,248,249,252],{},"没有用到官方的 ",[15,250,251],{},"@prisma/nuxt"," ，因为一开始装上报错了。",[43,254,258],{"className":255,"code":256,"language":257,"meta":48,"style":48},"language-shell shiki shiki-themes github-light","Environment variables loaded from .env\nPrisma schema loaded from prisma/schema.prisma\nError: \nThe \"path\" argument must be of type string. Received undefined\n","shell",[15,259,260,277,291,299],{"__ignoreMap":48},[52,261,262,265,268,271,274],{"class":54,"line":55},[52,263,264],{"class":58},"Environment",[52,266,267],{"class":116}," variables",[52,269,270],{"class":116}," loaded",[52,272,273],{"class":116}," from",[52,275,276],{"class":116}," .env\n",[52,278,279,281,284,286,288],{"class":54,"line":66},[52,280,240],{"class":58},[52,282,283],{"class":116}," schema",[52,285,270],{"class":116},[52,287,273],{"class":116},[52,289,290],{"class":116}," prisma/schema.prisma\n",[52,292,293,296],{"class":54,"line":74},[52,294,295],{"class":58},"Error:",[52,297,298],{"class":62}," \n",[52,300,301,304,307,310,313,316,319,322,325,328],{"class":54,"line":87},[52,302,303],{"class":58},"The",[52,305,306],{"class":116}," \"path\"",[52,308,309],{"class":116}," argument",[52,311,312],{"class":116}," must",[52,314,315],{"class":116}," be",[52,317,318],{"class":116}," of",[52,320,321],{"class":116}," type",[52,323,324],{"class":116}," string.",[52,326,327],{"class":116}," Received",[52,329,330],{"class":116}," undefined\n",[11,332,333,334,337,338,341,342,344,345,347,348,351],{},"后来单独安装了 ",[15,335,336],{},"prisma"," 和 ",[15,339,340],{},"@prisma/client"," 之后，发现有同样的问题。我也懒得再来一遍来，就按 ",[15,343,336],{}," 文档里流程接入。 总之，运行 ",[15,346,336],{}," 相关命令时添加上 ",[15,349,350],{},"npx prisma"," 就可以了。",[43,353,355],{"className":255,"code":354,"language":257,"meta":48,"style":48},"npm i prisma -D\n",[15,356,357],{"__ignoreMap":48},[52,358,359,362,365,368],{"class":54,"line":55},[52,360,361],{"class":58},"npm",[52,363,364],{"class":116}," i",[52,366,367],{"class":116}," prisma",[52,369,370],{"class":83}," -D\n",[43,372,374],{"className":255,"code":373,"language":257,"meta":48,"style":48},"npm i @prisma/client\n",[15,375,376],{"__ignoreMap":48},[52,377,378,380,382],{"class":54,"line":55},[52,379,361],{"class":58},[52,381,364],{"class":116},[52,383,384],{"class":116}," @prisma/client\n",[43,386,388],{"className":255,"code":387,"language":257,"meta":48,"style":48},"npx prisma init\n",[15,389,390],{"__ignoreMap":48},[52,391,392,395,397],{"class":54,"line":55},[52,393,394],{"class":58},"npx",[52,396,367],{"class":116},[52,398,399],{"class":116}," init\n",[11,401,402,403,405,406,337,409,412],{},"初始化完成后，会生成一个 ",[15,404,336],{}," 目录，里面有一个 ",[15,407,408],{},"dev.db",[15,410,411],{},"schema.prisma","。",[11,414,415,417,418,421,422,425],{},[15,416,408],{}," 是本地的数据库文件。如果你恰好也用 ",[15,419,420],{},"VS Code"," 的话，可以用 ",[15,423,424],{},"SQLite3 Editor","  这个免费的插件管理",[11,427,428],{},[37,429,430],{"href":430,"rel":431},"https://github.com/yy0931/sqlite3-editor",[41],[11,433,434],{},"好用的话别忘了给人家点个赞",[11,436,437],{},[438,439],"img",{"alt":48,"src":440},"https://img.zzao.club/article/202411261618324.png",[11,442,443,445],{},[15,444,411],{}," 就是我们代码和数据库之间的“桥梁”，里面定义了：",[447,448,449,453,456],"ul",{},[450,451,452],"li",{},"client：客户端配置",[450,454,455],{},"db：数据库配置，使用什么数据库， 链接地址",[450,457,458],{},"model：所有表结构",[11,460,461,464,465,468,469,472,473,476],{},[15,462,463],{},"client"," 里的 ",[15,466,467],{},"provider"," 是一个固定的值 ",[15,470,471],{},"prisma-client-js"," ，还有一个 ",[15,474,475],{},"binaryTargets"," 比较有用。",[43,478,480],{"className":45,"code":479,"language":47,"meta":48,"style":48},"generator client {\n  provider      = \"prisma-client-js\"\n  binaryTargets = [\"native\", \"debian-openssl-3.0.x\"]\n}\n",[15,481,482,487,498,520],{"__ignoreMap":48},[52,483,484],{"class":54,"line":55},[52,485,486],{"class":62},"generator client {\n",[52,488,489,492,495],{"class":54,"line":66},[52,490,491],{"class":62},"  provider      ",[52,493,494],{"class":104},"=",[52,496,497],{"class":116}," \"prisma-client-js\"\n",[52,499,500,503,505,508,511,514,517],{"class":54,"line":74},[52,501,502],{"class":62},"  binaryTargets ",[52,504,494],{"class":104},[52,506,507],{"class":62}," [",[52,509,510],{"class":116},"\"native\"",[52,512,513],{"class":62},", ",[52,515,516],{"class":116},"\"debian-openssl-3.0.x\"",[52,518,519],{"class":62},"]\n",[52,521,522],{"class":54,"line":87},[52,523,524],{"class":62},"}\n",[11,526,527,530,531,534,535,537],{},[15,528,529],{},"native"," 就是本机的环境，第二个值我配的是服务器上的环境，配好这个参数后，",[15,532,533],{},"prisma/client"," 里会生成对应的二进制文件，会被打包上去。（因为我这个项目是本地打包）如果你是线上打包的话，那 ",[15,536,529],{}," 其实就是你得线上服务器环境了。",[11,539,540],{},[438,541],{"alt":48,"src":542},"https://img.zzao.club/article/202411261618325.png",[11,544,545,548],{},[15,546,547],{},"db"," 主要是指定使用的数据库，以及地址。",[11,550,551,552,554],{},"这里我用 ",[15,553,21],{},"，所以我这样配置：",[43,556,558],{"className":45,"code":557,"language":47,"meta":48,"style":48},"datasource db {\n  provider = \"sqlite\"\n  url      = env(\"DATABASE_URL\")\n}\n",[15,559,560,565,575,594],{"__ignoreMap":48},[52,561,562],{"class":54,"line":55},[52,563,564],{"class":62},"datasource db {\n",[52,566,567,570,572],{"class":54,"line":66},[52,568,569],{"class":62},"  provider ",[52,571,494],{"class":104},[52,573,574],{"class":116}," \"sqlite\"\n",[52,576,577,580,582,585,588,591],{"class":54,"line":74},[52,578,579],{"class":62},"  url      ",[52,581,494],{"class":104},[52,583,584],{"class":58}," env",[52,586,587],{"class":62},"(",[52,589,590],{"class":116},"\"DATABASE_URL\"",[52,592,593],{"class":62},")\n",[52,595,596],{"class":54,"line":87},[52,597,524],{"class":62},[11,599,600,601,604,605,608,609,612,613,616],{},"其中 ",[15,602,603],{},"env(\"DATABASE_URL\")"," 是要读取你根目录下的 ",[15,606,607],{},".env"," 文件里的 ",[15,610,611],{},"DATABASE_URL","，所以说在部署时，要把 ",[15,614,615],{},"env文件"," 也发上去。",[11,618,619,620,622],{},"但 Nuxt3 里不推荐用 ",[15,621,615],{}," 管理环境变量，因为它本身要支持更多的部署环境，所以这里可以用其他方式来设置。",[11,624,625,626,629,630,633,634,637],{},"如果用 ",[15,627,628],{},"pm2"," 启动 ",[15,631,632],{},"node"," 服务（Nuxt output），那就可以在 ",[15,635,636],{},"ecosystem.config.cjs","，设置环境变量",[43,639,643],{"className":640,"code":641,"language":642,"meta":48,"style":48},"language-js shiki shiki-themes github-light","module.exports = {\n  apps: [\n    {\n      name: 'Blog',\n      port: '4577',\n      exec_mode: 'fork',\n      // instances: 'max',\n      script: './server/index.mjs',\n      // interpreter: '~/.bun/bin/bun',\n      env: {\n        NODE_ENV: 'production',\n      },\n      env_production: {\n        NODE_ENV: 'production',\n        DATABASE_URL: \"file:/your_path/data1/dbs/blog.db\"\n      }\n    }\n  ]\n}\n\n","js",[15,644,645,662,667,672,682,692,702,708,718,723,728,738,743,748,756,764,768,772,777],{"__ignoreMap":48},[52,646,647,650,653,656,659],{"class":54,"line":55},[52,648,649],{"class":83},"module",[52,651,652],{"class":62},".",[52,654,655],{"class":83},"exports",[52,657,658],{"class":104}," =",[52,660,661],{"class":62}," {\n",[52,663,664],{"class":54,"line":66},[52,665,666],{"class":62},"  apps: [\n",[52,668,669],{"class":54,"line":74},[52,670,671],{"class":62},"    {\n",[52,673,674,677,680],{"class":54,"line":87},[52,675,676],{"class":62},"      name: ",[52,678,679],{"class":116},"'Blog'",[52,681,120],{"class":62},[52,683,684,687,690],{"class":54,"line":93},[52,685,686],{"class":62},"      port: ",[52,688,689],{"class":116},"'4577'",[52,691,120],{"class":62},[52,693,694,697,700],{"class":54,"line":101},[52,695,696],{"class":62},"      exec_mode: ",[52,698,699],{"class":116},"'fork'",[52,701,120],{"class":62},[52,703,704],{"class":54,"line":110},[52,705,707],{"class":706},"sAwPA","      // instances: 'max',\n",[52,709,710,713,716],{"class":54,"line":123},[52,711,712],{"class":62},"      script: ",[52,714,715],{"class":116},"'./server/index.mjs'",[52,717,120],{"class":62},[52,719,720],{"class":54,"line":129},[52,721,722],{"class":706},"      // interpreter: '~/.bun/bin/bun',\n",[52,724,725],{"class":54,"line":140},[52,726,727],{"class":62},"      env: {\n",[52,729,730,733,736],{"class":54,"line":149},[52,731,732],{"class":62},"        NODE_ENV: ",[52,734,735],{"class":116},"'production'",[52,737,120],{"class":62},[52,739,740],{"class":54,"line":155},[52,741,742],{"class":62},"      },\n",[52,744,745],{"class":54,"line":161},[52,746,747],{"class":62},"      env_production: {\n",[52,749,750,752,754],{"class":54,"line":166},[52,751,732],{"class":62},[52,753,735],{"class":116},[52,755,120],{"class":62},[52,757,758,761],{"class":54,"line":174},[52,759,760],{"class":62},"        DATABASE_URL: ",[52,762,763],{"class":116},"\"file:/your_path/data1/dbs/blog.db\"\n",[52,765,766],{"class":54,"line":181},[52,767,158],{"class":62},[52,769,770],{"class":54,"line":190},[52,771,225],{"class":62},[52,773,774],{"class":54,"line":195},[52,775,776],{"class":62},"  ]\n",[52,778,779],{"class":54,"line":205},[52,780,524],{"class":62},[11,782,625,783,786,787,790,791,794,795,798],{},[15,784,785],{},"docker"," / ",[15,788,789],{},"docker-compose"," 同样的也是在 ",[15,792,793],{},"Dockerfile","、",[15,796,797],{},"docker-compose.yml"," 里配置好。",[11,800,801,804,805,808],{},[15,802,803],{},"model"," 里就是定义表结构。以一个 ",[15,806,807],{},"user"," 表为例",[43,810,812],{"className":45,"code":811,"language":47,"meta":48,"style":48},"model User {\n  id           Int           @id @default(autoincrement())\n  email        String?       @unique\n  username     String        @unique\n  nickname     String?\n  password     String\n  avatar_url   String?\n  articles     Articles[]\n\n  @@map(\"b_users\")\n}\n",[15,813,814,819,835,846,851,859,864,871,876,882,897],{"__ignoreMap":48},[52,815,816],{"class":54,"line":55},[52,817,818],{"class":62},"model User {\n",[52,820,821,824,827,829,832],{"class":54,"line":66},[52,822,823],{"class":62},"  id           Int           @id @",[52,825,826],{"class":58},"default",[52,828,587],{"class":62},[52,830,831],{"class":58},"autoincrement",[52,833,834],{"class":62},"())\n",[52,836,837,840,843],{"class":54,"line":74},[52,838,839],{"class":62},"  email        String",[52,841,842],{"class":104},"?",[52,844,845],{"class":62},"       @unique\n",[52,847,848],{"class":54,"line":87},[52,849,850],{"class":62},"  username     String        @unique\n",[52,852,853,856],{"class":54,"line":93},[52,854,855],{"class":62},"  nickname     String",[52,857,858],{"class":104},"?\n",[52,860,861],{"class":54,"line":101},[52,862,863],{"class":62},"  password     String\n",[52,865,866,869],{"class":54,"line":110},[52,867,868],{"class":62},"  avatar_url   String",[52,870,858],{"class":104},[52,872,873],{"class":54,"line":123},[52,874,875],{"class":62},"  articles     Articles[]\n",[52,877,878],{"class":54,"line":129},[52,879,881],{"emptyLinePlaceholder":880},true,"\n",[52,883,884,887,890,892,895],{"class":54,"line":140},[52,885,886],{"class":62},"  @@",[52,888,889],{"class":58},"map",[52,891,587],{"class":62},[52,893,894],{"class":116},"\"b_users\"",[52,896,593],{"class":62},[52,898,899],{"class":54,"line":149},[52,900,524],{"class":62},[11,902,903,906,907,910,911,914],{},[15,904,905],{},"model User"," ，这里的 ",[15,908,909],{},"User"," 会在其他的 model 中使用，如 ",[15,912,913],{},"user_info  User"," ：",[43,916,918],{"className":45,"code":917,"language":47,"meta":48,"style":48},"model Articles {\n  id         Int        @id @default(autoincrement())\n  uid        String     @unique\n  title      String\n  tags       String     @default(\"[]\")\n  create_ts  DateTime   @default(now())\n  updated_ts DateTime   @updatedAt\n  user_id    Int\n  user_info  User       @relation(fields: [user_id], references: [id], onDelete: NoAction)\n\n  @@map(\"b_articles\")\n}\n",[15,919,920,925,938,943,948,962,976,981,986,997,1001,1014],{"__ignoreMap":48},[52,921,922],{"class":54,"line":55},[52,923,924],{"class":62},"model Articles {\n",[52,926,927,930,932,934,936],{"class":54,"line":66},[52,928,929],{"class":62},"  id         Int        @id @",[52,931,826],{"class":58},[52,933,587],{"class":62},[52,935,831],{"class":58},[52,937,834],{"class":62},[52,939,940],{"class":54,"line":74},[52,941,942],{"class":62},"  uid        String     @unique\n",[52,944,945],{"class":54,"line":87},[52,946,947],{"class":62},"  title      String\n",[52,949,950,953,955,957,960],{"class":54,"line":93},[52,951,952],{"class":62},"  tags       String     @",[52,954,826],{"class":58},[52,956,587],{"class":62},[52,958,959],{"class":116},"\"[]\"",[52,961,593],{"class":62},[52,963,964,967,969,971,974],{"class":54,"line":101},[52,965,966],{"class":62},"  create_ts  DateTime   @",[52,968,826],{"class":58},[52,970,587],{"class":62},[52,972,973],{"class":58},"now",[52,975,834],{"class":62},[52,977,978],{"class":54,"line":110},[52,979,980],{"class":62},"  updated_ts DateTime   @updatedAt\n",[52,982,983],{"class":54,"line":123},[52,984,985],{"class":62},"  user_id    Int\n",[52,987,988,991,994],{"class":54,"line":129},[52,989,990],{"class":62},"  user_info  User       @",[52,992,993],{"class":58},"relation",[52,995,996],{"class":62},"(fields: [user_id], references: [id], onDelete: NoAction)\n",[52,998,999],{"class":54,"line":140},[52,1000,881],{"emptyLinePlaceholder":880},[52,1002,1003,1005,1007,1009,1012],{"class":54,"line":149},[52,1004,886],{"class":62},[52,1006,889],{"class":58},[52,1008,587],{"class":62},[52,1010,1011],{"class":116},"\"b_articles\"",[52,1013,593],{"class":62},[52,1015,1016],{"class":54,"line":155},[52,1017,524],{"class":62},[11,1019,1020,1021,337,1023,1026,1027,1029,1030,1032,1033,1035,1036,412],{},"字段、类型、说明这些规则就不说了，看看文档就能懂。这里 ",[15,1022,807],{},[15,1024,1025],{},"article"," 是一对多关系，每个 ",[15,1028,1025],{}," 只有一个 ",[15,1031,807],{},"，一个 ",[15,1034,807],{}," 可以有多个 ",[15,1037,1025],{},[11,1039,1040,1048,1049,1051],{},[1041,1042,1043,1044,1047],"strong",{},"这个 ",[15,1045,1046],{},"user_info"," 在表结构中不会存在，只是 prisma 会用到","，所以表里的字段是 ",[15,1050,1046],{}," 上面的那些。",[11,1053,1043,1054,1056,1057,1059],{},[15,1055,909],{},"，同样也会在使用 ",[15,1058,336],{}," 进行查询时使用，比如：",[43,1061,1063],{"className":45,"code":1062,"language":47,"meta":48,"style":48},"await prisma.user.findMany(...some options )\n",[15,1064,1065],{"__ignoreMap":48},[52,1066,1067,1070,1073,1076,1078,1081],{"class":54,"line":55},[52,1068,1069],{"class":104},"await",[52,1071,1072],{"class":62}," prisma.user.",[52,1074,1075],{"class":58},"findMany",[52,1077,587],{"class":62},[52,1079,1080],{"class":104},"...",[52,1082,1083],{"class":62},"some options )\n",[11,1085,1086,1087,1089,1090,1093,1094,1097],{},"如果要给 ",[15,1088,909],{}," 重新定义个名字，就使用 ",[15,1091,1092],{},"@@map"," ，这个名字（",[15,1095,1096],{},"b_users","）就是数据库真正的表名",[243,1099,1100],{"id":1100},"初始化和使用",[11,1102,1103,1104,1106,1107,1109],{},"定义好自己配置、表结构后，我们希望把定义好的结构同步到 ",[15,1105,408],{}," 里去，此时 ",[15,1108,408],{}," 是空的。",[43,1111,1113],{"className":255,"code":1112,"language":257,"meta":48,"style":48},"npx prisma migrate dev --name init\n",[15,1114,1115],{"__ignoreMap":48},[52,1116,1117,1119,1121,1124,1127,1130],{"class":54,"line":55},[52,1118,394],{"class":58},[52,1120,367],{"class":116},[52,1122,1123],{"class":116}," migrate",[52,1125,1126],{"class":116}," dev",[52,1128,1129],{"class":83}," --name",[52,1131,399],{"class":116},[11,1133,1134,1137,1138,1141],{},[15,1135,1136],{},"migrate"," 是迁移的命令，",[15,1139,1140],{},"dev"," 是在本地开发时表结构发生表更时生成一个迁移文件的命令。",[11,1143,1144,1145,1148,1149,1152],{},"生成的迁移文件会在 ",[15,1146,1147],{},"prisma/migrations"," 中 ",[15,1150,1151],{},"2024111100000_init/migration.sql"," 。",[11,1154,1155,1156,1159,1160,1162,1163,1152],{},"生成会自动执行一下 ",[15,1157,1158],{},"prisma generate","，此命令会安装 ",[15,1161,340],{},"并根据我们定义的模型，生成 ",[15,1164,1165],{},"client API",[11,1167,1168],{},"这就是为什么刚才的代码能顺利运行，并且提示的信息还特别全的原因（提供了完整的 TS 代码提示）：",[43,1170,1171],{"className":45,"code":1062,"language":47,"meta":48,"style":48},[15,1172,1173],{"__ignoreMap":48},[52,1174,1175,1177,1179,1181,1183,1185],{"class":54,"line":55},[52,1176,1069],{"class":104},[52,1178,1072],{"class":62},[52,1180,1075],{"class":58},[52,1182,587],{"class":62},[52,1184,1080],{"class":104},[52,1186,1083],{"class":62},[11,1188,1189],{},[438,1190],{"alt":48,"src":1191},"https://img.zzao.club/article/202411261623713.png",[11,1193,1194,1195,1197],{},"此时我们就可以使用 ",[15,1196,533],{}," 的 API 在代码中进行增删改查操作了。",[11,1199,1200,1201,1204,1205],{},"在此之前，如果还没有一个 prisma 实例，可以像我一样在 ",[15,1202,1203],{},"server"," 目录中创建一个 ",[15,1206,1207],{},"prisma.ts",[43,1209,1211],{"className":45,"code":1210,"language":47,"meta":48,"style":48},"import { PrismaClient } from '@prisma/client'\n\nconst prismaClientSingleton = () => {\n  return new PrismaClient()\n}\n\ndeclare const globalThis: {\n  prismaGlobal: ReturnType\u003Ctypeof prismaClientSingleton>;\n} & typeof global;\n\nconst prisma = globalThis.prismaGlobal ?? prismaClientSingleton()\n\nexport default prisma\n\nif (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma\n",[15,1212,1213,1227,1231,1249,1263,1267,1271,1287,1307,1321,1325,1343,1347,1358,1362],{"__ignoreMap":48},[52,1214,1215,1218,1221,1224],{"class":54,"line":55},[52,1216,1217],{"class":104},"import",[52,1219,1220],{"class":62}," { PrismaClient } ",[52,1222,1223],{"class":104},"from",[52,1225,1226],{"class":116}," '@prisma/client'\n",[52,1228,1229],{"class":54,"line":66},[52,1230,881],{"emptyLinePlaceholder":880},[52,1232,1233,1236,1239,1241,1244,1247],{"class":54,"line":74},[52,1234,1235],{"class":104},"const",[52,1237,1238],{"class":58}," prismaClientSingleton",[52,1240,658],{"class":104},[52,1242,1243],{"class":62}," () ",[52,1245,1246],{"class":104},"=>",[52,1248,661],{"class":62},[52,1250,1251,1254,1257,1260],{"class":54,"line":87},[52,1252,1253],{"class":104},"  return",[52,1255,1256],{"class":104}," new",[52,1258,1259],{"class":58}," PrismaClient",[52,1261,1262],{"class":62},"()\n",[52,1264,1265],{"class":54,"line":93},[52,1266,524],{"class":62},[52,1268,1269],{"class":54,"line":101},[52,1270,881],{"emptyLinePlaceholder":880},[52,1272,1273,1276,1279,1282,1285],{"class":54,"line":110},[52,1274,1275],{"class":104},"declare",[52,1277,1278],{"class":104}," const",[52,1280,1281],{"class":83}," globalThis",[52,1283,1284],{"class":104},":",[52,1286,661],{"class":62},[52,1288,1289,1293,1295,1298,1301,1304],{"class":54,"line":123},[52,1290,1292],{"class":1291},"sqxcx","  prismaGlobal",[52,1294,1284],{"class":104},[52,1296,1297],{"class":58}," ReturnType",[52,1299,1300],{"class":62},"\u003C",[52,1302,1303],{"class":104},"typeof",[52,1305,1306],{"class":62}," prismaClientSingleton>;\n",[52,1308,1309,1312,1315,1318],{"class":54,"line":129},[52,1310,1311],{"class":62},"} ",[52,1313,1314],{"class":104},"&",[52,1316,1317],{"class":104}," typeof",[52,1319,1320],{"class":62}," global;\n",[52,1322,1323],{"class":54,"line":140},[52,1324,881],{"emptyLinePlaceholder":880},[52,1326,1327,1329,1331,1333,1336,1339,1341],{"class":54,"line":149},[52,1328,1235],{"class":104},[52,1330,367],{"class":83},[52,1332,658],{"class":104},[52,1334,1335],{"class":62}," globalThis.prismaGlobal ",[52,1337,1338],{"class":104},"??",[52,1340,1238],{"class":58},[52,1342,1262],{"class":62},[52,1344,1345],{"class":54,"line":155},[52,1346,881],{"emptyLinePlaceholder":880},[52,1348,1349,1352,1355],{"class":54,"line":161},[52,1350,1351],{"class":104},"export",[52,1353,1354],{"class":104}," default",[52,1356,1357],{"class":62}," prisma\n",[52,1359,1360],{"class":54,"line":166},[52,1361,881],{"emptyLinePlaceholder":880},[52,1363,1364,1367,1370,1373,1376,1379,1382,1384],{"class":54,"line":174},[52,1365,1366],{"class":104},"if",[52,1368,1369],{"class":62}," (process.env.",[52,1371,1372],{"class":83},"NODE_ENV",[52,1374,1375],{"class":104}," !==",[52,1377,1378],{"class":116}," 'production'",[52,1380,1381],{"class":62},") globalThis.prismaGlobal ",[52,1383,494],{"class":104},[52,1385,1357],{"class":62},[11,1387,1388,1389,1392,1393],{},"然后在 ",[15,1390,1391],{},"server/api/v1/user"," 中（没有的话需要创建），新建一个接口文件 ",[15,1394,1395],{},"login.post.ts",[11,1397,1398,1399,1402,1403,1406],{},"此时 ",[15,1400,1401],{},"Nuxt"," 中会出现一个 POST 接口：",[15,1404,1405],{},"/api/v1/user/login"," ,（你可以在页面中或其他 api工具中调用测试一下）",[11,1408,1388,1409,1411,1412,1414,1415,1417],{},[15,1410,1395],{}," 中引入 ",[15,1413,336],{}," 进行使用 。当然，如果你用了官方的 module，这里直接使用 ",[15,1416,336],{}," 实例就可以了。",[43,1419,1421],{"className":45,"code":1420,"language":47,"meta":48,"style":48},"\nimport prisma from '~/server/prisma'\n\nexport default defineEventHandler(async (event) => {\n    // useSafeValidatedBody\n  const body = await readBody(event)\n  const { nuxtSecretKey, jwtSecret } = useRuntimeConfig(event)\n  const { username, password } = body\n  const secret = new TextEncoder().encode(jwtSecret)\n  const user = await prisma.user.findUnique({\n    where: {\n      username\n    }\n  })\n\n  return {\n    data: user,\n    msg: '登录成功'\n  }\n})\n",[15,1422,1423,1427,1439,1443,1470,1475,1494,1519,1540,1563,1582,1587,1592,1596,1601,1605,1611,1616,1624,1629],{"__ignoreMap":48},[52,1424,1425],{"class":54,"line":55},[52,1426,881],{"emptyLinePlaceholder":880},[52,1428,1429,1431,1434,1436],{"class":54,"line":66},[52,1430,1217],{"class":104},[52,1432,1433],{"class":62}," prisma ",[52,1435,1223],{"class":104},[52,1437,1438],{"class":116}," '~/server/prisma'\n",[52,1440,1441],{"class":54,"line":74},[52,1442,881],{"emptyLinePlaceholder":880},[52,1444,1445,1447,1449,1452,1454,1457,1460,1463,1466,1468],{"class":54,"line":87},[52,1446,1351],{"class":104},[52,1448,1354],{"class":104},[52,1450,1451],{"class":58}," defineEventHandler",[52,1453,587],{"class":62},[52,1455,1456],{"class":104},"async",[52,1458,1459],{"class":62}," (",[52,1461,1462],{"class":1291},"event",[52,1464,1465],{"class":62},") ",[52,1467,1246],{"class":104},[52,1469,661],{"class":62},[52,1471,1472],{"class":54,"line":93},[52,1473,1474],{"class":706},"    // useSafeValidatedBody\n",[52,1476,1477,1480,1483,1485,1488,1491],{"class":54,"line":101},[52,1478,1479],{"class":104},"  const",[52,1481,1482],{"class":83}," body",[52,1484,658],{"class":104},[52,1486,1487],{"class":104}," await",[52,1489,1490],{"class":58}," readBody",[52,1492,1493],{"class":62},"(event)\n",[52,1495,1496,1498,1501,1504,1506,1509,1512,1514,1517],{"class":54,"line":110},[52,1497,1479],{"class":104},[52,1499,1500],{"class":62}," { ",[52,1502,1503],{"class":83},"nuxtSecretKey",[52,1505,513],{"class":62},[52,1507,1508],{"class":83},"jwtSecret",[52,1510,1511],{"class":62}," } ",[52,1513,494],{"class":104},[52,1515,1516],{"class":58}," useRuntimeConfig",[52,1518,1493],{"class":62},[52,1520,1521,1523,1525,1528,1530,1533,1535,1537],{"class":54,"line":123},[52,1522,1479],{"class":104},[52,1524,1500],{"class":62},[52,1526,1527],{"class":83},"username",[52,1529,513],{"class":62},[52,1531,1532],{"class":83},"password",[52,1534,1511],{"class":62},[52,1536,494],{"class":104},[52,1538,1539],{"class":62}," body\n",[52,1541,1542,1544,1547,1549,1551,1554,1557,1560],{"class":54,"line":129},[52,1543,1479],{"class":104},[52,1545,1546],{"class":83}," secret",[52,1548,658],{"class":104},[52,1550,1256],{"class":104},[52,1552,1553],{"class":58}," TextEncoder",[52,1555,1556],{"class":62},"().",[52,1558,1559],{"class":58},"encode",[52,1561,1562],{"class":62},"(jwtSecret)\n",[52,1564,1565,1567,1570,1572,1574,1576,1579],{"class":54,"line":140},[52,1566,1479],{"class":104},[52,1568,1569],{"class":83}," user",[52,1571,658],{"class":104},[52,1573,1487],{"class":104},[52,1575,1072],{"class":62},[52,1577,1578],{"class":58},"findUnique",[52,1580,1581],{"class":62},"({\n",[52,1583,1584],{"class":54,"line":149},[52,1585,1586],{"class":62},"    where: {\n",[52,1588,1589],{"class":54,"line":155},[52,1590,1591],{"class":62},"      username\n",[52,1593,1594],{"class":54,"line":161},[52,1595,225],{"class":62},[52,1597,1598],{"class":54,"line":166},[52,1599,1600],{"class":62},"  })\n",[52,1602,1603],{"class":54,"line":174},[52,1604,881],{"emptyLinePlaceholder":880},[52,1606,1607,1609],{"class":54,"line":181},[52,1608,1253],{"class":104},[52,1610,661],{"class":62},[52,1612,1613],{"class":54,"line":190},[52,1614,1615],{"class":62},"    data: user,\n",[52,1617,1618,1621],{"class":54,"line":195},[52,1619,1620],{"class":62},"    msg: ",[52,1622,1623],{"class":116},"'登录成功'\n",[52,1625,1626],{"class":54,"line":205},[52,1627,1628],{"class":62},"  }\n",[52,1630,1631],{"class":54,"line":212},[52,1632,1633],{"class":62},"})\n",[11,1635,1636,1637,1640,1641,1644,1645,1644,1648,1651],{},"这里仅仅演示一下 ",[15,1638,1639],{},"client api"," 的使用。然后在 nuxt3 中，使用 ",[15,1642,1643],{},"$fetch"," 、",[15,1646,1647],{},"useAsyncData",[15,1649,1650],{},"useFetch"," 进行调用即可。",[243,1653,1654],{"id":1654},"表结构变更和同步",[11,1656,1657],{},"在开发阶段，表结构很有可能会变动。",[11,1659,1660,1661,1664,1665,1667,1668,1671],{},"这种情况下，只需要修改 ",[15,1662,1663],{},"prisma.schema"," 的 ",[15,1666,803],{}," 定义，然后再使用 ",[15,1669,1670],{},"migrate dev"," ，生成一条迁移文件。",[43,1673,1675],{"className":255,"code":1674,"language":257,"meta":48,"style":48},"npx prisma migrate dev\n",[15,1676,1677],{"__ignoreMap":48},[52,1678,1679,1681,1683,1685],{"class":54,"line":55},[52,1680,394],{"class":58},[52,1682,367],{"class":116},[52,1684,1123],{"class":116},[52,1686,1687],{"class":116}," dev\n",[11,1689,1690,1691,1693],{},"比如我的初始 ",[15,1692,909],{}," 是这样的 （init/migration.sql）",[43,1695,1699],{"className":1696,"code":1697,"language":1698,"meta":48,"style":48},"language-sql shiki shiki-themes github-light","-- CreateTable\nCREATE TABLE \"User\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"email\" TEXT NOT NULL,\n    \"name\" TEXT\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"User_email_key\" ON \"User\"(\"email\");\n\n","sql",[15,1700,1701,1706,1722,1739,1751,1759,1764,1768,1773],{"__ignoreMap":48},[52,1702,1703],{"class":54,"line":55},[52,1704,1705],{"class":706},"-- CreateTable\n",[52,1707,1708,1711,1714,1717,1719],{"class":54,"line":66},[52,1709,1710],{"class":104},"CREATE",[52,1712,1713],{"class":104}," TABLE",[52,1715,1716],{"class":62}," \"",[52,1718,909],{"class":58},[52,1720,1721],{"class":62},"\" (\n",[52,1723,1724,1727,1730,1733,1736],{"class":54,"line":74},[52,1725,1726],{"class":116},"    \"id\"",[52,1728,1729],{"class":104}," INTEGER",[52,1731,1732],{"class":104}," NOT NULL",[52,1734,1735],{"class":104}," PRIMARY KEY",[52,1737,1738],{"class":62}," AUTOINCREMENT,\n",[52,1740,1741,1744,1747,1749],{"class":54,"line":87},[52,1742,1743],{"class":116},"    \"email\"",[52,1745,1746],{"class":104}," TEXT",[52,1748,1732],{"class":104},[52,1750,120],{"class":62},[52,1752,1753,1756],{"class":54,"line":93},[52,1754,1755],{"class":116},"    \"name\"",[52,1757,1758],{"class":104}," TEXT\n",[52,1760,1761],{"class":54,"line":101},[52,1762,1763],{"class":62},");\n",[52,1765,1766],{"class":54,"line":110},[52,1767,881],{"emptyLinePlaceholder":880},[52,1769,1770],{"class":54,"line":123},[52,1771,1772],{"class":706},"-- CreateIndex\n",[52,1774,1775,1777,1780,1782,1785,1788,1791,1794,1796,1799],{"class":54,"line":129},[52,1776,1710],{"class":104},[52,1778,1779],{"class":104}," UNIQUE INDEX",[52,1781,1716],{"class":62},[52,1783,1784],{"class":58},"User_email_key",[52,1786,1787],{"class":62},"\" ",[52,1789,1790],{"class":104},"ON",[52,1792,1793],{"class":116}," \"User\"",[52,1795,587],{"class":62},[52,1797,1798],{"class":116},"\"email\"",[52,1800,1763],{"class":62},[11,1802,1803,1804,1806,1807],{},"然后我发现字段不够，或者要改，于是修改 model 后，运行 ",[15,1805,1670],{}," , 又生成了一条 ",[15,1808,1809],{},"migration.sql",[43,1811,1813],{"className":1696,"code":1812,"language":1698,"meta":48,"style":48},"/*\n  Warnings:\n\n  - Added the required column `password` to the `User` table without a default value. This is not possible if the table is not empty.\n\n*/\n-- RedefineTables\nPRAGMA foreign_keys=OFF;\nCREATE TABLE \"new_User\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"email\" TEXT NOT NULL,\n    \"name\" TEXT,\n    \"password\" TEXT NOT NULL,\n    \"role\" TEXT NOT NULL DEFAULT 'user'\n);\nINSERT INTO \"new_User\" (\"email\", \"id\", \"name\") SELECT \"email\", \"id\", \"name\" FROM \"User\";\nDROP TABLE \"User\";\nALTER TABLE \"new_User\" RENAME TO \"User\";\nCREATE UNIQUE INDEX \"User_email_key\" ON \"User\"(\"email\");\nPRAGMA foreign_key_check;\nPRAGMA foreign_keys=ON;\n\n",[15,1814,1815,1820,1825,1829,1834,1838,1843,1848,1859,1872,1884,1894,1902,1913,1928,1932,1977,1988,2007,2029,2034],{"__ignoreMap":48},[52,1816,1817],{"class":54,"line":55},[52,1818,1819],{"class":706},"/*\n",[52,1821,1822],{"class":54,"line":66},[52,1823,1824],{"class":706},"  Warnings:\n",[52,1826,1827],{"class":54,"line":74},[52,1828,881],{"emptyLinePlaceholder":880},[52,1830,1831],{"class":54,"line":87},[52,1832,1833],{"class":706},"  - Added the required column `password` to the `User` table without a default value. This is not possible if the table is not empty.\n",[52,1835,1836],{"class":54,"line":93},[52,1837,881],{"emptyLinePlaceholder":880},[52,1839,1840],{"class":54,"line":101},[52,1841,1842],{"class":706},"*/\n",[52,1844,1845],{"class":54,"line":110},[52,1846,1847],{"class":706},"-- RedefineTables\n",[52,1849,1850,1853,1856],{"class":54,"line":123},[52,1851,1852],{"class":62},"PRAGMA foreign_keys",[52,1854,1855],{"class":104},"=OFF",[52,1857,1858],{"class":62},";\n",[52,1860,1861,1863,1865,1867,1870],{"class":54,"line":129},[52,1862,1710],{"class":104},[52,1864,1713],{"class":104},[52,1866,1716],{"class":62},[52,1868,1869],{"class":58},"new_User",[52,1871,1721],{"class":62},[52,1873,1874,1876,1878,1880,1882],{"class":54,"line":140},[52,1875,1726],{"class":116},[52,1877,1729],{"class":104},[52,1879,1732],{"class":104},[52,1881,1735],{"class":104},[52,1883,1738],{"class":62},[52,1885,1886,1888,1890,1892],{"class":54,"line":149},[52,1887,1743],{"class":116},[52,1889,1746],{"class":104},[52,1891,1732],{"class":104},[52,1893,120],{"class":62},[52,1895,1896,1898,1900],{"class":54,"line":155},[52,1897,1755],{"class":116},[52,1899,1746],{"class":104},[52,1901,120],{"class":62},[52,1903,1904,1907,1909,1911],{"class":54,"line":161},[52,1905,1906],{"class":116},"    \"password\"",[52,1908,1746],{"class":104},[52,1910,1732],{"class":104},[52,1912,120],{"class":62},[52,1914,1915,1918,1920,1922,1925],{"class":54,"line":166},[52,1916,1917],{"class":116},"    \"role\"",[52,1919,1746],{"class":104},[52,1921,1732],{"class":104},[52,1923,1924],{"class":104}," DEFAULT",[52,1926,1927],{"class":116}," 'user'\n",[52,1929,1930],{"class":54,"line":174},[52,1931,1763],{"class":62},[52,1933,1934,1937,1940,1942,1944,1946,1949,1951,1954,1956,1959,1962,1964,1966,1968,1970,1973,1975],{"class":54,"line":181},[52,1935,1936],{"class":104},"INSERT INTO",[52,1938,1939],{"class":116}," \"new_User\"",[52,1941,1459],{"class":62},[52,1943,1798],{"class":116},[52,1945,513],{"class":62},[52,1947,1948],{"class":116},"\"id\"",[52,1950,513],{"class":62},[52,1952,1953],{"class":116},"\"name\"",[52,1955,1465],{"class":62},[52,1957,1958],{"class":104},"SELECT",[52,1960,1961],{"class":116}," \"email\"",[52,1963,513],{"class":62},[52,1965,1948],{"class":116},[52,1967,513],{"class":62},[52,1969,1953],{"class":116},[52,1971,1972],{"class":104}," FROM",[52,1974,1793],{"class":116},[52,1976,1858],{"class":62},[52,1978,1979,1982,1984,1986],{"class":54,"line":190},[52,1980,1981],{"class":104},"DROP",[52,1983,1713],{"class":104},[52,1985,1793],{"class":116},[52,1987,1858],{"class":62},[52,1989,1990,1993,1995,1997,2000,2003,2005],{"class":54,"line":195},[52,1991,1992],{"class":104},"ALTER",[52,1994,1713],{"class":104},[52,1996,1939],{"class":116},[52,1998,1999],{"class":62}," RENAME ",[52,2001,2002],{"class":104},"TO",[52,2004,1793],{"class":116},[52,2006,1858],{"class":62},[52,2008,2009,2011,2013,2015,2017,2019,2021,2023,2025,2027],{"class":54,"line":205},[52,2010,1710],{"class":104},[52,2012,1779],{"class":104},[52,2014,1716],{"class":62},[52,2016,1784],{"class":58},[52,2018,1787],{"class":62},[52,2020,1790],{"class":104},[52,2022,1793],{"class":116},[52,2024,587],{"class":62},[52,2026,1798],{"class":116},[52,2028,1763],{"class":62},[52,2030,2031],{"class":54,"line":212},[52,2032,2033],{"class":62},"PRAGMA foreign_key_check;\n",[52,2035,2036,2038,2041],{"class":54,"line":217},[52,2037,1852],{"class":62},[52,2039,2040],{"class":104},"=ON",[52,2042,1858],{"class":62},[11,2044,2045,2046,794,2048,2051,2052,2055,2056,2058,2059,2062],{},"我又加了 ",[15,2047,1532],{},[15,2049,2050],{},"role","，它的做法是新建一个 ",[15,2053,2054],{},"new_user"," 表，把原来的表的内容插入进去，然后把新的表名改一下。同时上方有提醒 我加了一个 ",[15,2057,1532],{}," ，但又",[1041,2060,2061],{},"不能为空","，如果原表有数据的话，就会有问题了。这个需要注意一下。",[11,2064,2065,2066],{},"此时，把新的表结构同步到线上的命令，使用的是：",[15,2067,2068],{},"prisma migrate deploy",[11,2070,2071,2072,2074,2075,2078],{},"如果数据库是空的、新的，那本地一直用 ",[15,2073,1670],{}," 生成变更，线上一直用 ",[15,2076,2077],{},"migrate deploy"," 就能一直同步（目前我没发现问题）",[11,2080,2081,2082,1644,2085],{},"但有一些情况是，项目可能是老的，数据库也已经存在。此时就需要 prisma 的其他命令，如 ",[15,2083,2084],{},"push",[15,2086,2087],{},"pull",[243,2089,2090],{"id":2090},"设置基线",[11,2092,2093],{},"如果已经有数据库了，数据库无法被重置。",[11,2095,2096,2097,2100],{},"就要用到 ",[1041,2098,2099],{},"基线"," 。只要不是刚接入数据库，可以都算这种情况。",[11,2102,2103],{},"先把现有的表结构拉下来。官方称之为 内省/反省 （==Introspection==）",[43,2105,2107],{"className":255,"code":2106,"language":257,"meta":48,"style":48},"prisma db pull\n",[15,2108,2109],{"__ignoreMap":48},[52,2110,2111,2113,2116],{"class":54,"line":55},[52,2112,336],{"class":58},[52,2114,2115],{"class":116}," db",[52,2117,2118],{"class":116}," pull\n",[11,2120,2121,2122,2124],{},"拉下来的结果，就和初始化时自己写 ",[15,2123,803],{}," 一样，prisma.schema 里会生成一堆和现有数据库对应的表结构。",[11,2126,2127,2128,2130],{},"如果已经有了 ",[15,2129,1147],{}," 文件夹，请删除、移动、重命名或备份此文件夹",[11,2132,2133,2134,2137,2138,2141,2142,2145,2146,2148,2149,2152],{},"新建",[15,2135,2136],{},"migrations","文件夹，新增一个 ",[15,2139,2140],{},"0_init"," 目录，注意：",[1041,2143,2144],{},"前缀是必须的","！,正常使用时是按时间戳排序的，这里需要重建一个",[1041,2147,2099],{},"，所以以 ",[1041,2150,2151],{},"0_"," 开头。",[43,2154,2156],{"className":255,"code":2155,"language":257,"meta":48,"style":48},"mkdir -p prisma/migrations/0_init\n",[15,2157,2158],{"__ignoreMap":48},[52,2159,2160,2163,2166],{"class":54,"line":55},[52,2161,2162],{"class":58},"mkdir",[52,2164,2165],{"class":83}," -p",[52,2167,2168],{"class":116}," prisma/migrations/0_init\n",[11,2170,2171,2172,2175,2176,412],{},"使用",[15,2173,2174],{},"migrate diff","生成一份",[15,2177,1809],{},[11,2179,2180,2181,2183,2184,2186],{},"个人理解：生成一份和当前数据库差异",[15,2182,1698],{},"，也就是运行这个",[15,2185,1698],{},"后可以达到目前这个数据库的结构。",[43,2188,2193],{"className":2189,"code":2191,"language":2192},[2190],"language-text","npx prisma migrate diff \\  \n--from-empty \\  \n--to-schema-datamodel prisma/schema.prisma \\  \n--script > prisma/migrations/0_init/migration.sql\n","text",[15,2194,2191],{"__ignoreMap":48},[11,2196,2197],{},[1041,2198,2199],{},"resolve 应用这个差异",[11,2201,2202,2203,2206],{},"个人理解：应用意为已经执行过了，相当于",[1041,2204,2205],{},"抹平了历史记录","，从现在开始的改动就是基于当前数据库结构的变更了。",[43,2208,2210],{"className":255,"code":2209,"language":257,"meta":48,"style":48},"npx prisma migrate resolve --applied 0_init\n",[15,2211,2212],{"__ignoreMap":48},[52,2213,2214,2216,2218,2220,2223,2226],{"class":54,"line":55},[52,2215,394],{"class":58},[52,2217,367],{"class":116},[52,2219,1123],{"class":116},[52,2221,2222],{"class":116}," resolve",[52,2224,2225],{"class":83}," --applied",[52,2227,2228],{"class":116}," 0_init\n",[11,2230,2231],{},"线上数据库同理。",[11,2233,2234,2235,2237],{},"后续有表结构的改动，就使用",[15,2236,1670],{},"来维护就可以了",[43,2239,2240],{"className":255,"code":1674,"language":257,"meta":48,"style":48},[15,2241,2242],{"__ignoreMap":48},[52,2243,2244,2246,2248,2250],{"class":54,"line":55},[52,2245,394],{"class":58},[52,2247,367],{"class":116},[52,2249,1123],{"class":116},[52,2251,1687],{"class":116},[11,2253,2254,2255,2257,2258,1459,2260,2263],{},"部署到服务器上时， 我是把 ",[15,2256,336],{}," 也直接",[15,2259,2084],{},[1041,2261,2262],{},"git",")到项目打包后的目录下了，同步表结构只需要:",[43,2265,2267],{"className":255,"code":2266,"language":257,"meta":48,"style":48},"npx prisma migrate deploy\n",[15,2268,2269],{"__ignoreMap":48},[52,2270,2271,2273,2275,2277],{"class":54,"line":55},[52,2272,394],{"class":58},[52,2274,367],{"class":116},[52,2276,1123],{"class":116},[52,2278,2279],{"class":116}," deploy\n",[11,2281,2282],{},"不知道关于这部分，我有没有讲清楚？",[243,2284,2285],{"id":2285},"总结",[11,2287,2288,2289,18,2291,2293,2294,2296],{},"以上就是在 ",[15,2290,17],{},[15,2292,336],{}," 管理 ",[15,2295,21],{}," 的一些经验分享。",[11,2298,2299],{},"后续还会有深入和具体的 Nuxt3 全栈项目内的使用，完全使用 Nuxt 的 Server 能力开发接口等内容还在编写中～",[11,2301,2302,2303,2306],{},"欢迎关注「",[1041,2304,2305],{},"早早集市","」",[2308,2309,2310],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}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 pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}",{"title":48,"searchDepth":66,"depth":66,"links":2312},[2313,2314,2315,2316,2317],{"id":245,"depth":66,"text":245},{"id":1100,"depth":66,"text":1100},{"id":1654,"depth":66,"text":1654},{"id":2090,"depth":66,"text":2090},{"id":2285,"depth":66,"text":2285},"2024-11-26T00:00:00.000Z","Nuxt3全栈开发时，如何使用Prisma来管理Sqlite、Mysql等数据库","md","2025-08-19T00:00:00.000Z",{},"/post/nuxt/nuxt3-full-stack-prisma-sqlite","---\ntitle: Nuxt3全栈开发 · 如何使用Prisma+Sqlite\ndate: 2024-11-26\nlastmod: 2025-08-19\ntags: [\"博客\", \"Nuxt\", \"Prisma\"]\nversions: [\"nuxt@3.14.0\", \"@prisma/client@5.22.0\"]\ndescription: Nuxt3全栈开发时，如何使用Prisma来管理Sqlite、Mysql等数据库\n---\n想要在 `Nuxt3` 中使用 `Sqlite` 非常简单，但重点是如何管理表结构的变更。\n\nNuxt3官方提供了配置，可以直接开启数据库，默认就是`sqlite`，数据库文件会存储在`.data/` 目录下。\n\nhttps://db0.unjs.io/connectors/sqlite\n\n```typescript\nnitro: {\n    experimental: {\n      database: true\n    },\n    database: {\n      default: {\n        connector: 'sqlite',\n        options: {\n          path: '/blog',\n          name: 'blog.db'\n        }\n      }\n    },\n    devDatabase: {\n      default: {\n        connector: 'sqlite',\n        options: {\n          path: '/Users/your_name/code/abc/databases/blog',\n          name: 'blog.db'\n        }\n      }\n    }\n  },\n```\n\n但是后续的表结构的更新、新增、删除等操作如何和线上同步就是一个大问题了。\n\n所以对于单机部署的博客来说，还是用上`Prisma`省心。 \n\n## 安装及配置\n\n没有用到官方的 `@prisma/nuxt` ，因为一开始装上报错了。\n\n```shell\nEnvironment variables loaded from .env\nPrisma schema loaded from prisma/schema.prisma\nError: \nThe \"path\" argument must be of type string. Received undefined\n```\n\n后来单独安装了 `prisma` 和 `@prisma/client` 之后，发现有同样的问题。我也懒得再来一遍来，就按 `prisma` 文档里流程接入。 总之，运行 `prisma` 相关命令时添加上 `npx prisma` 就可以了。\n\n```shell\nnpm i prisma -D\n```\n\n```shell\nnpm i @prisma/client\n```\n\n```shell\nnpx prisma init\n```\n\n初始化完成后，会生成一个 `prisma` 目录，里面有一个 `dev.db` 和 `schema.prisma`。 \n\n`dev.db` 是本地的数据库文件。如果你恰好也用 `VS Code` 的话，可以用 `SQLite3 Editor`  这个免费的插件管理\n\nhttps://github.com/yy0931/sqlite3-editor\n\n好用的话别忘了给人家点个赞\n\n![](https://img.zzao.club/article/202411261618324.png)\n\n`schema.prisma` 就是我们代码和数据库之间的“桥梁”，里面定义了：\n\n- client：客户端配置\n- db：数据库配置，使用什么数据库， 链接地址\n- model：所有表结构\n\n`client` 里的 `provider` 是一个固定的值 `prisma-client-js` ，还有一个 `binaryTargets` 比较有用。\n\n```typescript\ngenerator client {\n  provider      = \"prisma-client-js\"\n  binaryTargets = [\"native\", \"debian-openssl-3.0.x\"]\n}\n```\n\n`native` 就是本机的环境，第二个值我配的是服务器上的环境，配好这个参数后，`prisma/client` 里会生成对应的二进制文件，会被打包上去。（因为我这个项目是本地打包）如果你是线上打包的话，那 `native` 其实就是你得线上服务器环境了。\n\n![](https://img.zzao.club/article/202411261618325.png)\n\n`db` 主要是指定使用的数据库，以及地址。\n\n这里我用 `Sqlite`，所以我这样配置：\n\n```typescript\ndatasource db {\n  provider = \"sqlite\"\n  url      = env(\"DATABASE_URL\")\n}\n```\n\n其中 `env(\"DATABASE_URL\")` 是要读取你根目录下的 `.env` 文件里的 `DATABASE_URL`，所以说在部署时，要把 `env文件` 也发上去。\n\n但 Nuxt3 里不推荐用 `env文件` 管理环境变量，因为它本身要支持更多的部署环境，所以这里可以用其他方式来设置。\n\n如果用 `pm2` 启动 `node` 服务（Nuxt output），那就可以在 `ecosystem.config.cjs`，设置环境变量\n\n```js\nmodule.exports = {\n  apps: [\n    {\n      name: 'Blog',\n      port: '4577',\n      exec_mode: 'fork',\n      // instances: 'max',\n      script: './server/index.mjs',\n      // interpreter: '~/.bun/bin/bun',\n      env: {\n        NODE_ENV: 'production',\n      },\n      env_production: {\n        NODE_ENV: 'production',\n        DATABASE_URL: \"file:/your_path/data1/dbs/blog.db\"\n      }\n    }\n  ]\n}\n\n```\n\n如果用 `docker` / `docker-compose` 同样的也是在 `Dockerfile`、`docker-compose.yml` 里配置好。\n\n`model` 里就是定义表结构。以一个 `user` 表为例\n\n```typescript\nmodel User {\n  id           Int           @id @default(autoincrement())\n  email        String?       @unique\n  username     String        @unique\n  nickname     String?\n  password     String\n  avatar_url   String?\n  articles     Articles[]\n\n  @@map(\"b_users\")\n}\n```\n\n`model User` ，这里的 `User` 会在其他的 model 中使用，如 `user_info  User` ：\n\n```typescript\nmodel Articles {\n  id         Int        @id @default(autoincrement())\n  uid        String     @unique\n  title      String\n  tags       String     @default(\"[]\")\n  create_ts  DateTime   @default(now())\n  updated_ts DateTime   @updatedAt\n  user_id    Int\n  user_info  User       @relation(fields: [user_id], references: [id], onDelete: NoAction)\n\n  @@map(\"b_articles\")\n}\n```\n\n字段、类型、说明这些规则就不说了，看看文档就能懂。这里 `user` 和 `article` 是一对多关系，每个 `article` 只有一个 `user`，一个 `user` 可以有多个 `article`。\n\n**这个 `user_info` 在表结构中不会存在，只是 prisma 会用到**，所以表里的字段是 `user_info` 上面的那些。\n\n这个 `User`，同样也会在使用 `prisma` 进行查询时使用，比如：\n\n```typescript\nawait prisma.user.findMany(...some options )\n```\n\n如果要给 `User` 重新定义个名字，就使用 `@@map` ，这个名字（`b_users`）就是数据库真正的表名\n\n## 初始化和使用\n\n定义好自己配置、表结构后，我们希望把定义好的结构同步到 `dev.db` 里去，此时 `dev.db` 是空的。\n\n```shell\nnpx prisma migrate dev --name init\n```\n\n`migrate` 是迁移的命令，`dev` 是在本地开发时表结构发生表更时生成一个迁移文件的命令。\n\n生成的迁移文件会在 `prisma/migrations` 中 `2024111100000_init/migration.sql` 。\n\n生成会自动执行一下 `prisma generate`，此命令会安装 `@prisma/client`并根据我们定义的模型，生成 `client API` 。\n\n这就是为什么刚才的代码能顺利运行，并且提示的信息还特别全的原因（提供了完整的 TS 代码提示）：\n\n```typescript\nawait prisma.user.findMany(...some options )\n```\n\n![](https://img.zzao.club/article/202411261623713.png)\n\n此时我们就可以使用 `prisma/client` 的 API 在代码中进行增删改查操作了。\n\n在此之前，如果还没有一个 prisma 实例，可以像我一样在 `server` 目录中创建一个 `prisma.ts` \n\n```typescript\nimport { PrismaClient } from '@prisma/client'\n\nconst prismaClientSingleton = () => {\n  return new PrismaClient()\n}\n\ndeclare const globalThis: {\n  prismaGlobal: ReturnType\u003Ctypeof prismaClientSingleton>;\n} & typeof global;\n\nconst prisma = globalThis.prismaGlobal ?? prismaClientSingleton()\n\nexport default prisma\n\nif (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma\n```\n\n然后在 `server/api/v1/user` 中（没有的话需要创建），新建一个接口文件 `login.post.ts` \n\n此时 `Nuxt` 中会出现一个 POST 接口：`/api/v1/user/login` ,（你可以在页面中或其他 api工具中调用测试一下）\n\n然后在 `login.post.ts` 中引入 `prisma` 进行使用 。当然，如果你用了官方的 module，这里直接使用 `prisma` 实例就可以了。\n\n```typescript\n\nimport prisma from '~/server/prisma'\n\nexport default defineEventHandler(async (event) => {\n\t// useSafeValidatedBody\n  const body = await readBody(event)\n  const { nuxtSecretKey, jwtSecret } = useRuntimeConfig(event)\n  const { username, password } = body\n  const secret = new TextEncoder().encode(jwtSecret)\n  const user = await prisma.user.findUnique({\n    where: {\n      username\n    }\n  })\n\n  return {\n    data: user,\n    msg: '登录成功'\n  }\n})\n```\n\n这里仅仅演示一下 `client api` 的使用。然后在 nuxt3 中，使用 `$fetch` 、`useAsyncData` 、`useFetch` 进行调用即可。\n\n\n## 表结构变更和同步\n\n在开发阶段，表结构很有可能会变动。\n\n这种情况下，只需要修改 `prisma.schema` 的 `model` 定义，然后再使用 `migrate dev` ，生成一条迁移文件。\n\n```shell\nnpx prisma migrate dev\n```\n\n比如我的初始 `User` 是这样的 （init/migration.sql）\n\n```sql\n-- CreateTable\nCREATE TABLE \"User\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"email\" TEXT NOT NULL,\n    \"name\" TEXT\n);\n\n-- CreateIndex\nCREATE UNIQUE INDEX \"User_email_key\" ON \"User\"(\"email\");\n\n```\n\n然后我发现字段不够，或者要改，于是修改 model 后，运行 `migrate dev` , 又生成了一条 `migration.sql` \n\n```sql\n/*\n  Warnings:\n\n  - Added the required column `password` to the `User` table without a default value. This is not possible if the table is not empty.\n\n*/\n-- RedefineTables\nPRAGMA foreign_keys=OFF;\nCREATE TABLE \"new_User\" (\n    \"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n    \"email\" TEXT NOT NULL,\n    \"name\" TEXT,\n    \"password\" TEXT NOT NULL,\n    \"role\" TEXT NOT NULL DEFAULT 'user'\n);\nINSERT INTO \"new_User\" (\"email\", \"id\", \"name\") SELECT \"email\", \"id\", \"name\" FROM \"User\";\nDROP TABLE \"User\";\nALTER TABLE \"new_User\" RENAME TO \"User\";\nCREATE UNIQUE INDEX \"User_email_key\" ON \"User\"(\"email\");\nPRAGMA foreign_key_check;\nPRAGMA foreign_keys=ON;\n\n```\n\n我又加了 `password`、`role`，它的做法是新建一个 `new_user` 表，把原来的表的内容插入进去，然后把新的表名改一下。同时上方有提醒 我加了一个 `password` ，但又**不能为空**，如果原表有数据的话，就会有问题了。这个需要注意一下。\n\n此时，把新的表结构同步到线上的命令，使用的是：`prisma migrate deploy`\n\n如果数据库是空的、新的，那本地一直用 `migrate dev` 生成变更，线上一直用 `migrate deploy` 就能一直同步（目前我没发现问题）\n\n但有一些情况是，项目可能是老的，数据库也已经存在。此时就需要 prisma 的其他命令，如 `push` 、`pull` \n\n## 设置基线\n\n如果已经有数据库了，数据库无法被重置。\n\n就要用到 **基线** 。只要不是刚接入数据库，可以都算这种情况。\n\n先把现有的表结构拉下来。官方称之为 内省/反省 （==Introspection==）\n\n```shell\nprisma db pull\n```\n\n拉下来的结果，就和初始化时自己写 `model` 一样，prisma.schema 里会生成一堆和现有数据库对应的表结构。\n\n如果已经有了 `prisma/migrations` 文件夹，请删除、移动、重命名或备份此文件夹\n\n新建`migrations`文件夹，新增一个 `0_init` 目录，注意：**前缀是必须的**！,正常使用时是按时间戳排序的，这里需要重建一个**基线**，所以以 **0_** 开头。\n\n```shell\nmkdir -p prisma/migrations/0_init\n```\n\n使用`migrate diff`生成一份`migration.sql`。\n\n个人理解：生成一份和当前数据库差异`sql`，也就是运行这个`sql`后可以达到目前这个数据库的结构。\n\n```\nnpx prisma migrate diff \\  \n--from-empty \\  \n--to-schema-datamodel prisma/schema.prisma \\  \n--script > prisma/migrations/0_init/migration.sql\n```\n\n**resolve 应用这个差异**\n\n个人理解：应用意为已经执行过了，相当于**抹平了历史记录**，从现在开始的改动就是基于当前数据库结构的变更了。\n\n```shell\nnpx prisma migrate resolve --applied 0_init\n```\n\n线上数据库同理。\n\n后续有表结构的改动，就使用`migrate dev`来维护就可以了\n\n```shell\nnpx prisma migrate dev\n```\n\n部署到服务器上时， 我是把 `prisma` 也直接`push` (**git**)到项目打包后的目录下了，同步表结构只需要:\n\n```shell\nnpx prisma migrate deploy\n```\n\n不知道关于这部分，我有没有讲清楚？\n\n## 总结\n\n以上就是在 `Nuxt3` 中使用 `prisma` 管理 `Sqlite` 的一些经验分享。\n\n后续还会有深入和具体的 Nuxt3 全栈项目内的使用，完全使用 Nuxt 的 Server 能力开发接口等内容还在编写中～\n\n欢迎关注「**早早集市**」\n",{"title":5,"description":2319},"post/nuxt/Nuxt3-full-stack-prisma-sqlite",[2328,1401,240],"博客",[2330,2331],"nuxt@3.14.0","@prisma/client@5.22.0","SV1YcJhy0RrB1bCB9uMhtyz-rgIOAj7BdQVM7nPGHRk",[2334,2338],{"title":2335,"path":2336,"stem":2337},"OpenClaw 安装入门（Windows）","/post/zzao/openclaw/openclaw-install-windows","post/zzao/openclaw/openclaw-install-windows",{"title":2339,"path":2340,"stem":2341},"假设你是AI，你的Skill应该是什么样的","/post/zzao/ai-skill-structure","post/zzao/ai-skill-structure",1779005086578]