试试用Deno写一个RestApi

1,108 阅读3分钟

很多人都在关注Deno,我也不例外,毕竟是nodejs之父的又一大作。这篇文章将使用Deno写一个简单的TodoList的RestApi,尝尝鲜。

Deno是什么?

Deno是一个用Rust写的基于 V8 引擎的一个Javascript / Typescript 运行环境。

Deno Features

  • Use TypeScript or JavaScript
  • ES Modules
  • Secure by Default
  • Top Level / First Class Await
  • De-centralized Packages
  • Built In Testing
  • Standard Library
  • Browser Compatible API
  • Modern JS

项目编写

目录结构

  • controller
    • tasks.ts 定义控制器
  • router.ts 定义项目路由
  • server.ts 项目主入口,创建http服务器
  • tasks.http 测试api
  • types.ts 定义接口

由于只是尝尝鲜,数据采用的是静态的,没有连接数据库,就没有写service了。

主要逻辑

Server.ts

import { Application } from 'https://deno.land/x/oak/mod.ts'
import router from './router.ts'
const port = 5000

const app = new Application()

app.use(router.routes())
app.use(router.allowedMethods())

console.log(`Server running on port ${port}`)

await app.listen({ port })

router.ts

import { Router } from 'https://deno.land/x/oak/mod.ts'
import { getTasks, getTask, updateTask, deleteTask, addTask } from './controller/tasks.ts'
const router = new Router()


router.get('/api/v1/tasks', getTasks)
      .get('/api/v1/tasks/:id', getTask)
      .post('/api/v1/tasks', addTask)
      .put('/api/v1/tasks/:id', updateTask)
      .delete('/api/v1/tasks/:id', deleteTask)

export default router

/contraller/tasks.ts

import { v4 } from 'https://deno.land/std/uuid/mod.ts'
import { Task } from '../types.ts'

let tasks: Task[] = [
    {
        id: "1",
        name: "学习Deno",
        description: "使用Deno写个小案例",
        completed: false
    },
    {
        id: "2",
        name: "学习Nodejs",
        description: "使用Nodejs写个小案例",
        completed: false
    },
    {
        id: "3",
        name: "学习Vue",
        description: "使用Vue写个小案例",
        completed: false
    },
    {
        id: "4",
        name: "学习React",
        description: "使用React写个小案例",
        completed: false
    },
    {
        id: "5",
        name: "学习Angular",
        description: "使用Angular写个小案例",
        completed: false
    }
]

/** @desc   get all tasks
 *  @route  GET /api/v1/tasks
 */
const getTasks = ({ response }: { response: any }) => {
    response.body = {
        success: true,
        data: tasks
    }
}

/** @desc   get single task
 *  @route  GET /api/v1/tasks/:id
 */
const getTask = ({ params, response }: { params: {id: string} ,response: any }) => {
    const task: Task | undefined = tasks.find(t => t.id === params.id)
    if(task) {
        response.status = 200
        response.body = {
            success: true,
            data: task
        }
    } else {
        response.status = 404
        response.body = {
            success: false,
            message: 'No task found'
        }
    }
}

/** @desc   add task
 *  @route  POST /api/v1/tasks
 */
const addTask = async ({ request, response }: { request: any,response: any }) => {
    const body = await request.body()
    if(!request.hasBody){
        response.status = 400
        response.body = {
            success: false,
            message: 'No data'
        }
    } else {
        const task: Task = body.value
        task.id = v4.generate()
        tasks.push(task)
        response.status = 201
        response.body = {
            success: true,
            data: task
        }
    }
}

/** @desc   update task by id
 *  @route  PUT /api/v1/tasks/:id
 */
const updateTask = async ({ params, request, response }: {  request: any, params: {id: string}, response: any }) => {
    const index: number = tasks.findIndex(t => t.id === params.id)
    if(index !== -1) {
        const body = await request.body()

        const updateData: {name?: string, description?: string, completed?: boolean } = body.value
        tasks[index] = {...tasks[index], ...updateData}

        response.status = 200
        response.body = {
            success: true,
            data: tasks[index]
        }
    } else {
        response.status = 404
        response.body = {
            success: false,
            message: 'No task found'
        }
    }
}

/** @desc   Delete task by id
 *  @route  DELETE /api/v1/tasks/:id
 */
const deleteTask = ({ params, response }: {  params: {id: string},response: any }) => {
    const index: number = tasks.findIndex(t => t.id === params.id)
    if(index !== -1) {
        tasks.splice(index,1)
        response.status = 200
        response.body = {
            success: true,
            message: 'Task Removed'
        }
    } else {
        response.status = 404
        response.body = {
            success: false,
            message: 'No task found'
        }
    } 
}

export { getTasks, getTask, addTask, updateTask, deleteTask }

可以看到较express、koa的写法差别都不是特别大。

API测试

@host = http://localhost:5000/api/v1
### GET ALl tasks
GET {{host}}/tasks HTTP/1.1
### Get single task
GET {{host}}/tasks/1 HTTP/1.1
### add task
POST {{host}}/tasks HTTP/1.1
Content-Type: application/json

{
    "name": "学习Electron",
    "description": "使用Electron写个小案例",
    "completed": false
}
### update task
PUT {{host}}/tasks/1 HTTP/1.1
Content-Type: application/json

{
    "name": "测试",
    "description": "测试更新接口",
    "completed": true
}
### delete task
DELETE {{host}}/tasks/1 HTTP/1.1

总结

我们不断优化Deno的http服务器的性能。对于'hello world'的例子,DenoHTTP服务器每秒处理约25,000个请求,最大延迟1.3ms。Node服务器每秒处理34,000个请求,最大延迟介于2~300ms之间。

这段话摘自Deno官网。按照官网的说法deno的http性能会比node好很多,不过就目前来说还是很难取代node的。node的生态太丰富了。就我个人的看法来说,我比较喜欢deno的地方在于:

  • 内置了TypeScript编译器,写起来很爽啊~~
  • 取消掉了package.json管理依赖的模式,不再依靠npm。(npm最让我头痛的一点在于包的下载时不时就会出错,每写一个项目就有一个node_modules)
  • 有官方的标准库,可以根据官方的标准库进行开发自己的东西。标准库始终跟着版本走,不会出现第三方库用爱发电,发着发着就不更新了的情况,这点我觉得很好。而且第三方开发的库,始终有些隐患。
  • http服务器性能高!!!

总之可以期待一下Deno,后面生态丰富了可以尝试去转Deno。

欢迎来一起交流👏。