前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Nuxt3 实战 (十):使用 Supabase 实现 RESTful 风格 API 接口

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

原创
作者头像
白雾茫茫丶
发布2024-06-19 14:30:41
1304
发布2024-06-19 14:30:41
举报
文章被收录于专栏:Nuxt3 实战系列Nuxt3 实战系列

前言

本篇文章我们来使用 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

数据录入的时候 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 文件:

代码语言:javascript
复制
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 文件:

代码语言:javascript
复制
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 文件:

代码语言:javascript
复制
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 文件:

代码语言:javascript
复制
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、 前端调用方式

代码语言:html
复制
<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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 表设计
  • 接口设计
  • 前端实现
  • 效果预览
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档