Deno@v1.0浅尝todos-list API

362 阅读1分钟

Deno@v1.0 稳定版虽然刚发布不久,但Deno社区已经出现一大批跟随者,基于deno的设计理念,本人比较看好deno的前景,所以接下来我们用deno来做个简单的todo-API小例子

安装:(本项目基于Mac环境开发)

安装asdf多版本管理工具(类似nodejs中的NVM):

  brew install asdf

配置环境变量: ~/.zshrc

. $(brew --prefix asdf)/asdf.sh

安装asdf插件

 asdf plugin-add deno https://github.com/asdf-community/asdf-deno.git

安装deno最新版本

asdf install deno lastest

查看当前deno版本:

 deno --version

由于项目简单,所有我们直接在根目录创建两个文件:

|- index.ts
|- data.json

index.ts

import { Application, Context, Router } from "https://deno.land/x/oak/mod.ts";
import { green, cyan, bold, yellow } from 'https://deno.land/std@0.51.0/fmt/colors.ts';

interface Todo {
    id: string;
    title: string;
    completed: boolean;
}
const app = new Application();
const router = new Router();

// 一些配置
const config = {
    hostname: '127.0.0.1',
    port: 8899,
    mockData: './data.json'
}
// 路由
router.get('/', async(ctx, next) => {
    ctx.response.body = 'todos list';
})

// 获取todo-list
router.get('/todos', async (ctx, next) => {
    const { response } = ctx;
    try {
        const res = await Deno.readFile(config.mockData);
        const resData = JSON.parse(new TextDecoder().decode(res));
        response.body = {
            status: 'ok',
            data: resData
}
    } catch (err) {
        console.log(err);
        response.status = 500;
        response.body = {
            status: 'fail',
            data: []
        }
    }
});

router.get('/todos/:id', async(ctx, next) => {
    const { request, response, params } = ctx;
    if(!params.id) {
        response.status = 400;
        response.body = {
            status: 'Invalid id'
        };
        return
    }
    try {
        const res = await Deno.readFile(config.mockData);
        const resData = JSON.parse(new TextDecoder().decode(res));
        const getItemData = resData.filter((item: any) => item.id === params.id);
        response.body = {
            status: 'ok',
            data: getItemData
        } 
    } catch (error) {
        console.log(error);
        response.status = 500;
        response.body = {
            status: 'fail',
            data: []
        }
    }
});
// 创建新todo
router.post('/todos', async (ctx, next) => {
    const { request, response } = ctx;
    if(!request.hasBody) {
        response.status = 400;
        response.body = {
            status: 'Invalid request'
        }
        return
    }
    try {
        console.log('reqBody:',await request.body())
        const req = await request.body();
        const reqTitle = req.value && req.value.title;
        const res = await Deno.readFile(config.mockData);
        const resData = JSON.parse(new TextDecoder().decode(res));
        const resDataLen = resData.length;
        resData.push({
            id: resDataLen + 1,
            title: reqTitle,
            completed: false
        });
        await Deno.writeFile(config.mockData, new TextEncoder().encode(JSON.stringify(resData)));
        response.status = 201;
    } catch (err) {
        console.log(err);
        response.status = 502;
        response.body = {
            status: 'Failed to create a new todo',
            error: err
        }
    }
});

// 更新todo
router.put('/todos/:id', async(ctx, next) => {
    const { request, response, params } = ctx
    if(!params.id) {
        response.status = 400;
        response.body = {
            status: 'Invalid id'
        };
        return
    }
    if(!request.hasBody) {
        response.status = 400;
        response.body = {
            status: 'Invalid request'
        };
        return
    }
    try {
        const { value } = await request.body();
        const [[_1, title], [_2, completed]] = value;
        const res = await Deno.readFile(config.mockData);
        const resData = JSON.parse(new TextDecoder().decode(res));
        const updateData = resData.map((updateItem: any) => {
            if(updateItem.id === params.id) {
                return {
                    ...updateItem, title, completed
                }
            }
            return updateItem
        });
        await Deno.writeFile(config.mockData, new TextEncoder().encode(JSON.stringify(updateData)));
        response.status = 204
    } catch (error) {
        console.log(error);
        response.status = 502;
        response.body = {
            status: 'Failed to update',
            error
        }
    }
});

// 删除todo
router.delete('/todos/:id', async(ctx, next) => {
    const {request, response, params} = ctx;
    if(!params.id) {
        response.status = 400;
        response.body = {
            status: 'Invalid id'
        }
        return
    }
    try {
        const res = await Deno.readFile(config.mockData);
        const resData = JSON.parse(new TextDecoder().decode(res));
        const updateData = resData.filter((item: any) => item.id !== params.id);
        await Deno.writeFile(config.mockData, new TextEncoder().encode(JSON.stringify(updateData)));
        response.status = 200;
        response.body = {
            status: 'ok',
            data: updateData
        };
    } catch (error) {
        console.log(error);
        response.status = 502;
        response.body = {
            status: 'Faild to update',
            error
        };
    }
});

// Logger
app.use(async (ctx, next) => {
    const { request, response } = ctx;
    await next();
    const rt = response.headers.get('X-Response-Time');
    console.log(
        `${green(request.method)} ${cyan(request.url.pathname)} - ${bold(String(rt))}`
    );
});

// Response Time
app.use(async (ctx, next) => {
    const start = Date.now();
    await next();
    const ms = Date.now() - start;
    ctx.response.headers.set('X-Response-Time', `${ms}ms`);
});

app.use(router.routes());
app.use(router.allowedMethods());
app.use((ctx, next) => {
    const { request, response } = ctx;
    response.status = 404;
    response.body = 
    `<html>
        <head></head>
        <body>
            <h1>404 - Not Found</h1>
            <p>Path <code>${request.url}</code> not found.</p>
        </body>
    </html>`;
});

console.log(
    bold('Server is running in') + yellow(`${config.hostname}:${config.port}`)
    );

await app.listen({ port: config.port });
console.log(bold('Finished.'))

由于deno@v1.0.0版本下对项目中依赖的https://deno.land/x/oak/mod.ts会报错,所以我们根据官方oak-issues中提供的建议,通过安装deno@v1.0.0-rc3版本来临时解决这个问题(就如deno.land官方所说,not all of Deno's features are ready for production yet)

data.json

[
 {"id":5,"title": "AAAAAAAAAA", "completed": false},
 {"id":15,"title": "BBBBBBBBBB", "completed": false}
 {"id":25,"title": "CCCCCCCCCC", "completed": false}
 {"id":35,"title": "DDDDDDDDDD", "completed": false}
]

在项目根目录执行如下命令运行起来:

deno run --allow-net --allow-read --allow-write  index.ts

然后我们就可以通过postman 或者 Paw 模拟API接口请求了,是不是很简单呢~~