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

简介: 这篇文章介绍了如何使用Supabase实现RESTful风格的API接口,用于网站分类和子站点的增删改查(CURD)功能。文章首先阐述了表设计,包括ds_categorys和ds_websites两张表的列名、类型和用途,并提到了为每张表添加的user_id和email字段以支持用户身份识别。接着,文章描述了接口设计,以ds_websites表为例,说明了如何通过RESTful API实现CURD功能,并给出了使用SupabaseClient实现插入数据的相关代码。文章最后提供了项目效果预览和总结,指出学习了Nuxt3创建接口及调用Supabase数据库操作。

前言

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

表设计

这里需要用到两张表:

1、 ds_categorys:存储网站分类

列名 类型 备注
id uuid 主键,分类 id
name text 分类名称
desc text 分类描述
sort int2 排序

2、 ds_websites:存储网站分类子站点

列名 类型 备注
id uuid 主键,站点 id
name text 站点名称
desc text 站点描述
category_id uuid 所属分类 id
url text 站点 url
logo text 站点 logo
tags text 站点标签
sort int2 排序

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

列名 类型 备注
user_id auth.uid() 登录用户的 uuid
email text 登录用户的 email

j67tl014dwcc2vx407m20sog2478u9e2.png

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

接口设计

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

接口 Methods 备注
/api/websites Get 读取
/api/websites Post 新增
/api/websites Put 更新
/api/websites Delete 删除

前端实现

阅读 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
      }
    }
    })
    
  2. 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
    }
    })
    
  3. 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
    }
    })
    
  4. 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: '请求成功'
    }
    })
    
  5. 前端调用方式

    <script setup lang="ts">
    const {
          
           data } = await useFetch('/api/websites')
    </script>
    
    <template>
    <pre>{
        
        { data }}</pre>
    </template>
    

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

效果预览

1stxxcrznta7g5l8xdiv0nco939kt81c.gif

总结

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

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

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

Github 仓库dream-site

线上预览dream-site.cn

相关文章
|
5天前
|
SQL 缓存 测试技术
构建高性能RESTful API:最佳实践与避坑指南###
—— 本文深入探讨了构建高性能RESTful API的关键技术要点,从设计原则、状态码使用、版本控制到安全性考虑,旨在为开发者提供一套全面的最佳实践框架。通过避免常见的设计陷阱,本文将指导你如何优化API性能,提升用户体验,确保系统的稳定性和可扩展性。 ###
37 12
|
2天前
|
JSON JavaScript API
深入浅出Node.js:从零开始构建RESTful API
【10月更文挑战第39天】 在数字化时代的浪潮中,API(应用程序编程接口)已成为连接不同软件应用的桥梁。本文将带领读者从零基础出发,逐步深入Node.js的世界,最终实现一个功能完备的RESTful API。通过实践,我们将探索如何利用Node.js的异步特性和强大的生态系统来构建高效、可扩展的服务。准备好迎接代码和概念的碰撞,一起解锁后端开发的新篇章。
|
2天前
|
JSON API 数据格式
淘宝 / 天猫官方商品 / 订单订单 API 接口丨商品上传接口对接步骤
要对接淘宝/天猫官方商品或订单API,需先注册淘宝开放平台账号,创建应用获取App Key和App Secret。之后,详细阅读API文档,了解接口功能及权限要求,编写认证、构建请求、发送请求和处理响应的代码。最后,在沙箱环境中测试与调试,确保API调用的正确性和稳定性。
|
8天前
|
JSON BI API
商城上货API接口的实战案例
在商城上货过程中,API接口扮演着至关重要的角色。以下是对商城上货API接口的实战分析,涵盖其主要功能、类型、安全性以及实战案例等方面。
|
4天前
|
XML 数据可视化 API
商品详情数据实战案例,API接口系列
淘宝商品详情数据在电商领域具有广泛的应用价值,而淘宝商品详情API接口则为开发者提供了获取这些数据的重要途径。通过合理利用这些接口和数据,可以提升业务效率、优化用户体验,为电商行业的发展注入新的活力。
|
5天前
|
存储 API 开发者
深入理解RESTful API设计原则
本文探讨了RESTful API的设计原则,强调了其在现代Web服务中的重要性。通过分析状态表示转移(REST)的概念、核心约束以及最佳实践,本文旨在为开发者提供构建高效、可扩展和易于维护的API的指导。文章还讨论了常见的设计陷阱和如何避免它们,以确保API设计的健壮性和灵活性。
|
6天前
|
JSON 缓存 API
构建高效RESTful API的最佳实践
【10月更文挑战第34天】在数字时代的浪潮中,后端开发扮演着至关重要的角色。本文将带你深入探索如何构建高效的RESTful API,从设计原则到实际编码技巧,再到性能优化和错误处理,我们将一一解锁这些技能。你将学会如何打造一个既优雅又强大的后端服务,让你的应用程序在激烈的市场竞争中脱颖而出。那么,让我们一起踏上这段精彩的旅程吧!
17 2
|
8天前
|
XML JSON API
【PHP开发专栏】PHP RESTful API设计与开发
随着互联网技术的发展,前后端分离成为Web开发的主流模式。本文介绍RESTful API的基本概念、设计原则及在PHP中的实现方法。RESTful API是一种轻量级、无状态的接口设计风格,通过HTTP方法(GET、POST、PUT、DELETE)操作资源,使用JSON或XML格式传输数据。在PHP中,通过定义路由、创建控制器、处理HTTP请求和响应等步骤实现RESTful API,并强调了安全性的重要性。
15 2
|
9天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
22 1
|
5天前
|
JSON API 数据安全/隐私保护
拍立淘按图搜索API接口返回数据的JSON格式示例
拍立淘按图搜索API接口允许用户通过上传图片来搜索相似的商品,该接口返回的通常是一个JSON格式的响应,其中包含了与上传图片相似的商品信息。以下是一个基于淘宝平台的拍立淘按图搜索API接口返回数据的JSON格式示例,同时提供对其关键字段的解释

热门文章

最新文章