Nuxt3 实战 (十):使用 Supabase 实现 RESTful 风格 API 接口

前言

本篇文章我们来使用 Supabase 实现 RESTful 风格的 API 接口,以此来实现网站分类和子站点的 CURD 功能。

表设计

这里需要用到两张表:

  1. ds_categorys:存储网站分类
列名类型备注
iduuid主键,分类 id
nametext分类名称
desctext分类描述
sortint2排序
  1. ds_websites:存储网站分类子站点
列名类型备注
iduuid主键,站点 id
nametext站点名称
desctext站点描述
category_iduuid所属分类 id
urltext站点 url
logotext站点 logo
tagstext站点标签
sortint2排序

这里需要注意的是,因为 Supabase 使用的是 postgresql 的 Row Level Security (RLS),一些数据库的操作对应不同的策略,这里我们还应该为每张表加上两个字段:

列名类型备注
user_idauth.uid()登录用户的 uuid
emailtext登录用户的 email

在这里插入图片描述

数据录入的时候 user_id 会自动填充,但是 email 需要在前台带入

接口设计

这里以 ds_websites 表为例,前台需要实现 CURD 功能,为此我们把接口设计成 RESTful 风格:

接口Methods备注
/api/websitesGet读取
/api/websitesPost新增
/api/websitesPut更新
/api/websitesDelete删除

前端实现

阅读 Nuxt3 中文文档,我们可以在 server/api 目录下新增接口。
在这里插入图片描述

  1. Get 接口server/api 目录下新建 index.get.ts 文件:
 import type { Response, PageResponse, WebsiteList, WebsiteParams } from '~/types'
 import { serverSupabaseClient } from '#supabase/server'
 import { RESPONSE_STATUS_CODE } from '~/enum'

 export default defineEventHandler(async (event): Promise<Response<PageResponse<WebsiteList>>> => {
   const client = await serverSupabaseClient(event)
   // 获取请求参数
   const { current, pageSize, name = '', category_id = '' } = getQuery(event) as WebsiteParams
   // 判断参数
   if (!current || !pageSize) {
     return { code: RESPONSE_STATUS_CODE.FAIL, msg: '参数错误' }
   }

   // 计算分页
   const start = (current - 1) * pageSize
   const end = current * pageSize - 1

   // 查询 sql
   let sqlQuery = client
     .from('ds_websites')
     .select('*,ds_categorys(*)', { count: 'exact' })
     .range(start, end)
     .order('sort', {
       ascending: false
     })
     .order('created_at', {
       ascending: false
     })

   // 判断查询参数
   if (name) {
     sqlQuery = sqlQuery.like('name', `%${name}%`)
   }
   if (category_id) {
     sqlQuery = sqlQuery.eq('category_id', category_id)
   }

   // 请求列表
   const { data, error, count } = await sqlQuery

   // 判断请求结果
   if (error) {
     throw createError({
       statusCode: RESPONSE_STATUS_CODE.FAIL,
       statusMessage: error.message
     })
   }

   // 请求成功
   return {
     code: RESPONSE_STATUS_CODE.SUCCESS,
     msg: '请求成功',
     data: {
       list: data,
       total: count
     }
   }
 })
  1. Post 接口server/api 目录下新建 index.post.ts 文件:
 import type { Response, WebsiteEdit, WebsiteList } from '~/types'
 import { serverSupabaseClient, serverSupabaseUser } from '#supabase/server'
 import { RESPONSE_STATUS_CODE } from '~/enum'

 export default defineEventHandler(async (event): Promise<Response<WebsiteList[]>> => {
   const client = await serverSupabaseClient<WebsiteList>(event)
   const user = await serverSupabaseUser(event)
   // 得到请求体
   const body: WebsiteEdit = await readBody(event)

   // 插入数据
   const { data, error } = await client
     .from('ds_websites')
     .insert({ ...body, email: user?.email })
     .select()

   // 判断请求结果
   if (error) {
     // 23505 是 PostgreSQL 的唯一性违反错误码
     if (error.code === '23505') {
       return {
         code: RESPONSE_STATUS_CODE.FAIL,
         msg: '站点名称已存在!'
       }
     } else {
       throw createError({
         statusCode: RESPONSE_STATUS_CODE.FAIL,
         statusMessage: error.message
       })
     }
   }

   // 请求成功
   return {
     code: RESPONSE_STATUS_CODE.SUCCESS,
     msg: '请求成功',
     data: data
   }
 })
  1. Put 接口server/api 目录下新建 index.put.ts 文件:
 import type { Response, WebsiteEdit, WebsiteList } from '~/types'
 import { serverSupabaseClient } from '#supabase/server'
 import { RESPONSE_STATUS_CODE } from '~/enum'

 export default defineEventHandler(async (event): Promise<Response<WebsiteList[]>> => {
   const client = await serverSupabaseClient<WebsiteList>(event)
   // 得到请求体
   const { id, ...body }: WebsiteEdit = await readBody(event)

   if (!id) {
     return {
       code: RESPONSE_STATUS_CODE.FAIL,
       msg: 'id不能为空!'
     }
   }

   // 插入数据
   const { data, error } = await client
     .from('ds_websites')
     .update({ ...body, updated_at: new Date() })
     .eq('id', id)
     .select()

   // 判断请求结果
   if (error) {
     // 23505 是 PostgreSQL 的唯一性违反错误码
     if (error.code === '23505') {
       return {
         code: RESPONSE_STATUS_CODE.FAIL,
         msg: '站点名称已存在!'
       }
     } else {
       throw createError({
         statusCode: RESPONSE_STATUS_CODE.FAIL,
         statusMessage: error.message
       })
     }
   }

   // 请求成功
   return {
     code: RESPONSE_STATUS_CODE.SUCCESS,
     msg: '请求成功',
     data: data
   }
 })
  1. Delete 接口server/api 目录下新建 index.delete.ts 文件:
 import type { Response, WebsiteEdit, WebsiteList } from '~/types'
 import { serverSupabaseClient } from '#supabase/server'
 import { RESPONSE_STATUS_CODE } from '~/enum'

 export default defineEventHandler(async (event): Promise<Response<WebsiteList[]>> => {
   const client = await serverSupabaseClient<WebsiteList>(event)
   // 得到请求体
   const { id }: WebsiteEdit = await readBody(event)

   if (!id) {
     return {
       code: RESPONSE_STATUS_CODE.FAIL,
       msg: 'id不能为空!'
     }
   }

   // 删除数据
   const { error } = await client.from('ds_websites').delete().eq('id', id)

   // 判断请求结果
   if (error) {
     throw createError({
       statusCode: RESPONSE_STATUS_CODE.FAIL,
       statusMessage: error.message
     })
   }

   // 请求成功
   return {
     code: RESPONSE_STATUS_CODE.SUCCESS,
     msg: '请求成功'
   }
 })
  1. 前端调用方式
<script setup lang="ts">
const { data } = await useFetch('/api/websites')
</script>

<template>
 <pre>{{ data }}</pre>
</template>

接口的相关逻辑,自己可以根据实际情况修改,具体的数据库操作文档可参考: Supabase API DOCS

效果预览

在这里插入图片描述

总结

本篇文章我们学到了以下知识:

  1. Nuxt3 如何创建接口并调用
  2. Supabase 数据库的基本操作和表的创建

到这里,项目的整体框架就已经出来了,后续我们要做的就是添加数据和完善优化,并根据自己爱好添加一些自己喜欢的功能。

Github 仓库:dream-site

线上预览:dream-site.cn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/725048.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

重生之 SpringBoot3 入门保姆级学习(22、场景整合 Swagger 接口文档)

重生之 SpringBoot3 入门保姆级学习&#xff08;22、场景整合 Swagger 接口文档&#xff09; 6.2 Swagger 接口文档 6.2 Swagger 接口文档 1、将 starter 导入 Maven 官网 https://springdoc.org/<dependency><groupId>org.springdoc</groupId><artifact…

学习记录之数学表达式(5)

文章目录 十、线性回归10.1 示例10.2 拟合10.3 推导10.4 岭回归10.5 作业 十一、Logistic回归11.1 分割超平面11.2 点到直线的距离11.3 sigmoid函数11.4 优化目标11.5 求解11.6 作业 十、线性回归 线性回归是一个常用的机器学习算法&#xff1b; 10.1 示例 表 1.单变量的股价预…

格雷母线技术革新:推动斗轮堆取料机进入精准操作时代

随着工业4.0时代的到来&#xff0c;智能化、自动化已成为工业发展的必然趋势。特别是在港口、电力、冶金等行业中&#xff0c;散料装卸机械的智能化水平直接关系到整个生产流程的效率与安全。斗轮堆取料机作为这些行业中的关键设备&#xff0c;其操作方式的革新显得尤为重要。 …

Unity OpenCVForUnity 安装和第二个案例详解 <二>

目录 一、前言 二、场景介绍 1.WebCamTextureToMatExample脚本 2.FpsMonitor脚本 三、 结构体Scaler 四、找到相机并使用 1.相机的启用 2.格式转换 a.把webCamTexture转换成Mat b.把Mat转换成Texture2D 五、脚本组合 六、作者的碎碎念 一、前言 第二个案例&#xf…

leetcode (top100)盛最多水的容器

题目&#xff1a; 题解&#xff1a; 第一种可行的方案&#xff1a; 设置左指针指向第一条线&#xff0c;设置右指针指向最后一条线。每次向中间移动两条线中最短的一条&#xff0c;计算移动过程中最大接水量。 本题可以看出影响接水量的有两个因素&#xff0c;两条线的距离&…

空间复杂度的相关概念

1. 空间复杂度 空间复杂度&#xff08;space complexity&#xff09;用于衡量算法占用内存空间随着数据量变大时的增长趋势。 统计哪些空间&#xff1a; ● 暂存数据&#xff1a;用于保存算法运行过程中的各种常量、变量、对象等。 ● 栈帧空间&#xff1a;用于保存调用函数…

PyTorch -- RNN 快速实践

RNN Layer torch.nn.RNN(input_size,hidden_size,num_layers,batch_first) input_size: 输入的编码维度hidden_size: 隐含层的维数num_layers: 隐含层的层数batch_first: True 指定输入的参数顺序为&#xff1a; x&#xff1a;[batch, seq_len, input_size]h0&#xff1a;[batc…

Ubuntu 24.04安装zabbix7.0.0图形中文乱码

当zabbix安装完成后&#xff0c;设置中文界面时&#xff0c;打开图形&#xff0c;中文内容会显示方框乱码&#xff0c;是因为服务器字体中没有相关的中文字体&#xff0c;需要更换。 1、找到中文字体&#xff0c;可以在网络上下载《得意黑》开源字体&#xff0c;也可以在windo…

LeetCode322.零钱兑换(一)

LeetCode刷题记录 文章目录 &#x1f4dc;题目描述&#x1f4a1;解题思路⌨C代码 &#x1f4dc;题目描述 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。…

SAP MIGO 050 BADI:字段 GOITEM-XXXXX 未准备好输出

背景&#xff1a; MIGO过账时候需要根据某些条件更改某些字段的值&#xff0c;当要改的字段在前台不显示时&#xff0c;通过MB_MIGO_BADI~LINE_MODIFY去更改时&#xff0c;则会出现以下报错&#xff1a;MIGO050 解决方案1&#xff1a; 通过配置将该字段配置显示出来即可&…

阿里云如何部署项目【2024 详细版】

首次注册阿里云后可以购买免费服务器&#xff0c;可以用服务器练习部署项目&#xff0c;这里以部署个人网站为例 本人目前没有购买域名&#xff0c;因此域名流程并没有写&#xff0c;有看不懂的私信或者评论就行&#xff0c;我都可以看见 目录 一、购买服务器 二、安装宝塔…

「Python-docx 专栏」docx设置罗马数字页码,即页码编码格式为罗马数字

本文目录 前言一、docx 设置罗马数字页码1、docx设置大写罗马数字的页码①、docx背后的xml长啥样②、<w:sectPr> 标签详解③、通过<w:sectPr> 设置大写罗马数字的页码A、完整代码B、处理效果图C、这段代码实际上的作用2、docx设置小写罗马数字的页码①、完整代码②…

vue3前端对接后端的图片验证码

vue3前端对接后端的图片验证码 <template> <image :src"captchaUrl" alt"图片验证码" click"refreshCaptcha"></image> </template><script setup>import {ref} from "vue";import {useCounterStore} …

vue 2.0

自定义vue标签指令&#xff1a; <!DOCTYPE html> <html lang"en"> <script src"vue.js"></script> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <div id…

Prometheus告警Alertmanager部署

Prometheus告警Alertmanager部署 资源监控一般离不开预警&#xff0c;因为我们不可能每时每刻都盯着某个资源监控看&#xff0c;而且在实际的工作中当中我们搭建的解决方案涉及到的服务器是多台甚至数十台&#xff0c;所以更加不现实&#xff0c;因此资源告警是一个必不可少的…

3ds Max软件下载安装:3D建模软件 轻松开启你的建模之旅!

3ds Max&#xff0c;在建模过程中&#xff0c;网格建模和NURBS建模两大技术发挥着不可或缺的作用。网格建模允许用户通过顶点、边和面等元素的调整&#xff0c;精确地塑造出模型的形态&#xff1b;而NURBS建模则以其优秀的曲线和曲面处理能力&#xff0c;为设计师们提供了更为平…

ChinaTravel成流量密码,景区如何打造视频监控管理平台提升旅游体验

随着中国经济的飞速发展和人民生活水平的持续提高&#xff0c;旅游已经成为越来越多人休闲放松的首选方式。近期&#xff0c;随着互联网的普及和社交媒体的兴起&#xff0c;以及免签政策带火入境游&#xff0c;“ChinaTravel”已成为社交网络上的一大流量密码&#xff0c;吸引了…

1. ELK日志分析

ELK日志分析 一、ELK作用、组件1、作用2、核心组件2.1 beat软件2.1 Logstash2.2 Elasticsearch2.3 Kibana 二、ELK部署、测试1、环境规划2、确保SELinux关闭、时间同步3、所有主机添加主机名解析4、三台ES主机安装jdk 1.155、调整系统资源限制6、部署es集群6.1 创建普通用户elk…

AI口语练习APP的技术难点

AI口语练习APP旨在帮助用户练习口语&#xff0c;因此其核心功能是语音识别和语音评测。以下是一些AI口语练习APP的主要技术难点。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 语音识别 语音识别是将语音信号转换为文本的过程。…

C++ —— unordered_set、unordered_map的介绍及使用

目录 unordered系列关联式容器 unordered_set的介绍 unordered_set的使用 unordered_set的定义方式 unordered_set接口的使用 unordered_multiset unordered_map的介绍 unordered_map的使用 unordered_map的定义方式 unordered_map接口的使用 unordered_multimap …