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

相关文章
|
4天前
|
API 网络架构 UED
构建RESTful API的最佳实践
【8月更文挑战第54天】在数字化时代,RESTful API已成为连接不同软件系统、提供数据服务的关键桥梁。本文将深入探讨如何构建高效、可维护的RESTful API,涵盖设计原则、安全策略和性能优化等关键方面。通过具体代码示例,我们将一步步展示如何实现一个简洁、直观且功能强大的API。无论你是新手还是有经验的开发者,这篇文章都将为你提供宝贵的指导和启示。
52 33
|
1天前
|
缓存 监控 测试技术
深入理解RESTful API设计原则与最佳实践
【9月更文挑战第26天】在数字化时代,API(应用程序编程接口)已成为连接不同软件和服务的桥梁。本文将深入浅出地介绍RESTful API的设计哲学、六大约束条件以及如何将这些原则应用到实际开发中,以实现高效、可维护和易于扩展的后端服务。通过具体实例,我们将探索如何避免常见设计陷阱,确保API设计的优雅与实用性并存。无论你是API设计的新手还是经验丰富的开发者,这篇文章都将为你提供宝贵的指导和启示。
|
4天前
|
存储 JSON API
实战派教程!Python Web开发中RESTful API的设计哲学与实现技巧,一网打尽!
在数字化时代,Web API成为连接前后端及构建复杂应用的关键。RESTful API因简洁直观而广受欢迎。本文通过实战案例,介绍Python Web开发中的RESTful API设计哲学与技巧,包括使用Flask框架构建一个图书管理系统的API,涵盖资源定义、请求响应设计及实现示例。通过准确使用HTTP状态码、版本控制、错误处理及文档化等技巧,帮助你深入理解RESTful API的设计与实现。希望本文能助力你的API设计之旅。
21 3
|
4天前
|
存储 前端开发 API
告别繁琐,拥抱简洁!Python RESTful API 设计实战,让 API 调用如丝般顺滑!
在 Web 开发的旅程中,设计一个高效、简洁且易于使用的 RESTful API 是至关重要的。今天,我想和大家分享一次我在 Python 中进行 RESTful API 设计的实战经历,希望能给大家带来一些启发。
14 3
|
2天前
|
缓存 前端开发 API
深入浅出:RESTful API设计的最佳实践
【9月更文挑战第24天】在数字化浪潮中,API作为连接不同软件组件的桥梁,其设计质量直接影响到系统的可维护性、扩展性及用户体验。本文将通过浅显易懂的语言,结合生动的比喻和实例,带领读者深入理解RESTful API设计的核心原则与最佳实践,旨在帮助开发者构建更加健壮、灵活且用户友好的后端服务。
|
3天前
|
开发框架 JSON 缓存
震撼发布!Python Web开发框架下的RESTful API设计全攻略,让数据交互更自由!
在数字化浪潮推动下,RESTful API成为Web开发中不可或缺的部分。本文详细介绍了在Python环境下如何设计并实现高效、可扩展的RESTful API,涵盖框架选择、资源定义、HTTP方法应用及响应格式设计等内容,并提供了基于Flask的示例代码。此外,还讨论了版本控制、文档化、安全性和性能优化等最佳实践,帮助开发者实现更流畅的数据交互体验。
17 1
|
5天前
|
JSON API 开发者
惊!Python Web开发新纪元,RESTful API设计竟能如此性感撩人?
在这个Python Web开发的新纪元里,RESTful API的设计已经超越了简单的技术实现,成为了一种追求极致用户体验和开发者友好的艺术表达。通过优雅的URL设计、合理的HTTP状态码使用、清晰的错误处理、灵活的版本控制以及严格的安全性措施,我们能够让RESTful API变得更加“性感撩人”,为Web应用注入新的活力与魅力。
18 3
|
5天前
|
JSON API 网络架构
掌握RESTful API设计的艺术:从理论到实践
【9月更文挑战第22天】在数字化时代,API已成为连接不同软件和服务的桥梁。本文将引导你了解如何设计高效、可扩展且易于使用的RESTful API,涵盖从基础概念到高级最佳实践的全方位知识。我们将一起探索REST原则、HTTP方法的正确应用、资源命名策略等关键元素,并配以实际代码示例,让你能够将理论转化为实际操作,构建出既符合标准又能应对复杂业务需求的API。
|
5天前
|
设计模式 Java API
Quarkus RESTful API性能揭秘:如何打造极速响应的应用程序?
在互联网高速发展的背景下,企业对应用性能的要求日益提升。Quarkus作为一款基于Java的开源框架,以出色的性能和简洁的设计模式成为开发者的首选。本文通过实例展示如何利用Quarkus构建响应迅速的RESTful API应用。首先创建Maven项目并配置Quarkus依赖,接着逐步实现用户管理系统的各个模块,包括实体类、数据访问层、服务层及资源层,最终完成一个高性能的RESTful API。通过Quarkus,开发者可以更高效地开发出轻量级且响应快速的应用程序。
20 1
|
XML 缓存 JSON
前后端分离开发,RESTful 接口应该这样设计
前后端分离开发,RESTful 接口应该这样设计
379 0
前后端分离开发,RESTful 接口应该这样设计