学习没有框架(如Express)的Vanilla Node.js REST API

253 阅读7分钟

一个没有框架(如Express)的香草式Node.js REST API

Node.js是一个非常流行的JavaScript框架。它在用于构建后端服务和API时最为闪亮。Node.js开发者经常利用开源框架和库(如Express.js)来开发应用程序。这些库在NPM注册表中是现成的。

每当你使用这些包时,就会有很多抽象;因此你不会利用Node.js的核心功能。你的应用程序的低级逻辑Node.js是隐藏的,因为这些包在幕后处理和执行原始Node.js。

需要注意的一个关键问题是,这些包使Node.js成为一种流行的技术。在另一边,你可以选择使用核心Node.js来开发你的应用程序。这样一来,你就可以利用Vanilla Node.js的功能。本博客将教你如何使用没有框架的vanilla Node.js来构建简单的API。

目标

在本指南中,我们将使用Node.js本身的核心功能构建一个简单的REST API。我们只是使用原始的[Node.js]和[HTTP模块]来创建和管理一个服务器。

这意味着我们将不使用NPM。因此,没有NPM相关的依赖,没有package.json ,没有package-lock.json ,也没有node_module 文件夹。

我们的目标是向你展示Node.js如何以最纯粹的形式工作,以及你如何在没有外部库或框架的情况下使用它。

注意:在任何实际项目中工作时,最好使用Node.js库和包。这样,你将充分利用现成的代码,使你的开发工作流程更简单、更快速。

前提条件

  • 确保你的电脑上安装了[Node.js]和[Postman]。
  • 熟悉如何使用[Postman]。
  • 对[Node.js]有基本了解。
  • 对[REST APIs]和[CRUD操作]的基本了解。
  • 对[JavaScript]的基本了解。本指南使用[ES6]功能和语法,如[箭头函数](=>)。

设置一个简单的HTTP服务器

在创建REST API之前,我们先创建一个简单的HTTP API,为Hi there语句提供服务。

创建一个项目文件夹和一个app.js 文件。

  1. 首先要做的是使用require() 方法从Node.js拉出HTTP模块。这个模块是Node.js的原生模块。你不需要任何额外的包或库来访问它,只需要在你的计算机上安装Node.js运行时间。
const http = require("http");

这样,我们使必要的方法和功能可用来建立一个服务器。

  1. 一旦可用,定义你希望服务器运行的端口,如下图所示。
const PORT = process.env.PORT || 5000;
  1. 为了创建服务器,你需要从HTTP模块中调用createServer 方法。即:http.createServer 。传递一个响应和一个请求,提供你的信息。

然后使用。

  • req.url 来设置请求的访问路线/URL。
  • req.method.
  • res.writeHead 设置任何响应头信息。
  • res.write() 发送响应的实际内容。
  • res.end() 来结束响应。
const server = http.createServer(async (req, res) => {
    //set the request route
    if (req.url === "/api" && req.method === "GET") {
        //response headers
        res.writeHead(200, { "Content-Type": "application/json" });
        //set the response
        res.write("Hi there, This is a Vanilla Node.js API");
        //end the response
        res.end();
    }

    // If no route present
    else {
        res.writeHead(404, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ message: "Route not found" }));
    }
});
  1. 调用listen() 方法并传入PORT 变量。然后添加一个console.log() 信息,表明服务器已经启动并运行。
server.listen(PORT, () => {
    console.log(`server started on port: ${PORT}`);
});
  1. 服务器已经设置好了。运行node app.js 来测试。这将在你的命令屏幕上记录console.log() 消息。

A simple server api

  1. 如果你在浏览器上打开http://localhost:5000/api ,你将会得到在 中定义的响应。res.write()

A simple server response

设置REST API

现在让我们看看如何使用原始Node.js设置REST API。我们将使用一个todos模板来演示。

下面是项目的结构。

\---vanilla-nodejs-rest-api
|   app.js
|   controller.js
|   data.js
|   utils.js

No sub-folders exist

添加测试数据

data.js: 保存一些临时测试数据。这些信息被保存在一个todos数组中。每个todo都有一个唯一的ID,一个todo标题,一个简短的描述,以及一个标志着完成todo的布尔值。

//data.js
/** Todos List*/
const todos = [
    {
        id: 1,
        title: "Coding in Javascript",
        description: "Working with functions in JavaScript",
        completed: false,
    },
    {
        id: 2,
        title: "Cooking Supper",
        description: "Preparing rice and chicken",
        completed: false,
    },
    {
        id: 3,
        title: "Taking a walk",
        description: "Easy time at the park",
        completed: false,
    },
    {
        id: 4,
        title: "Watching Netflix",
        description: "Enjoying the new premiered series",
        completed: false,
    },
];
module.exports = todos;

设置控制器

controllers.js:它管理本应用程序中使用的每个路由背后的实际功能和逻辑。它是由Controller 类组成的,它将有以下主要的HTTP方法。

  • getTodos(): 获取并列出临时data.js 文件中列出的所有todos。
  • getTodo(): 获取并列出一个单一的todo的唯一ID。
  • createTodo():创建一个新的临时todo。
  • updateTodo(): 更新现有todo的值。
  • deleteTodo():从列表中删除一个todo。
// controller.js
// Logic behind the functionalities
const data = require("./data");

class Controller {
    // getting all todos
    async getTodos() {
        // return all todos
        return new Promise((resolve, _) => resolve(data));
    }

    // getting a single todo
    async getTodo(id) {
        return new Promise((resolve, reject) => {
            // get the todo
            let todo = data.find((todo) => todo.id === parseInt(id));
            if (todo) {
                // return the todo
                resolve(todo);
            } else {
                // return an error
                reject(`Todo with id ${id} not found `);
            }
        });
    }

    // creating a todo
    async createTodo(todo) {
        return new Promise((resolve, _) => {
            // create a todo, with random id and data sent
            let newTodo = {
                id: Math.floor(4 + Math.random() * 10),
                ...todo,
            };

            // return the new created todo
            resolve(newTodo);
        });
    }

    // updating a todo
    async updateTodo(id) {
        return new Promise((resolve, reject) => {
            // get the todo.
            let todo = data.find((todo) => todo.id === parseInt(id));
            // if no todo, return an error
            if (!todo) {
                reject(`No todo with id ${id} found`);
            }
            //else, update it by setting completed to true
            todo["completed"] = true;
            // return the updated todo
            resolve(todo);
        });
    }

    // deleting a todo
    async deleteTodo(id) {
        return new Promise((resolve, reject) => {
            // get the todo
            let todo = data.find((todo) => todo.id === parseInt(id));
            // if no todo, return an error
            if (!todo) {
                reject(`No todo with id ${id} found`);
            }
            // else, return a success message
            resolve(`Todo deleted successfully`);
        });
    }
}
module.exports = Controller;

实用程序设置

utils.js。控制一个标准的Web API用例。它包括getReqData() 函数,该函数从服务器上的客户端检索数据。

//utils.js
function getReqData(req) {
    return new Promise((resolve, reject) => {
        try {
            let body = "";
            // listen to data sent by client
            req.on("data", (chunk) => {
                // append the string version to the body
                body += chunk.toString();
            });
            // listen till the end
            req.on("end", () => {
                // send back the data
                resolve(body);
            });
        } catch (error) {
            reject(error);
        }
    });
}
module.exports = { getReqData };

设置服务器和路由

app.js:这包含了。

  • 服务器的初始化和配置。
  • 适当的路由监听服务器的不同的HTTP方法。
  • 一个用于监听的端口号,并在浏览器上设置服务器。
//app.js
const http = require("http");
const Todo = require("./controller");
const { getReqData } = require("./utils");

const PORT = process.env.PORT || 5000;

const server = http.createServer(async (req, res) => {
    // /api/todos : GET
    if (req.url === "/api/todos" && req.method === "GET") {
        // get the todos.
        const todos = await new Todo().getTodos();
        // set the status code, and content-type
        res.writeHead(200, { "Content-Type": "application/json" });
        // send the data
        res.end(JSON.stringify(todos));
    }

    // /api/todos/:id : GET
    else if (req.url.match(/\/api\/todos\/([0-9]+)/) && req.method === "GET") {
        try {
            // get id from url
            const id = req.url.split("/")[3];
            // get todo
            const todo = await new Todo().getTodo(id);
            // set the status code and content-type
            res.writeHead(200, { "Content-Type": "application/json" });
            // send the data
            res.end(JSON.stringify(todo));
        } catch (error) {
            // set the status code and content-type
            res.writeHead(404, { "Content-Type": "application/json" });
            // send the error
            res.end(JSON.stringify({ message: error }));
        }
    }

    // /api/todos/:id : DELETE
    else if (req.url.match(/\/api\/todos\/([0-9]+)/) && req.method === "DELETE") {
        try {
            // get the id from url
            const id = req.url.split("/")[3];
            // delete todo
            let message = await new Todo().deleteTodo(id);
            // set the status code and content-type
            res.writeHead(200, { "Content-Type": "application/json" });
            // send the message
            res.end(JSON.stringify({ message }));
        } catch (error) {
            // set the status code and content-type
            res.writeHead(404, { "Content-Type": "application/json" });
            // send the error
            res.end(JSON.stringify({ message: error }));
        }
    }

    // /api/todos/:id : UPDATE
    else if (req.url.match(/\/api\/todos\/([0-9]+)/) && req.method === "PATCH") {
        try {
            // get the id from the url
            const id = req.url.split("/")[3];
            // update todo
            let updated_todo = await new Todo().updateTodo(id);
            // set the status code and content-type
            res.writeHead(200, { "Content-Type": "application/json" });
            // send the message
            res.end(JSON.stringify(updated_todo));
        } catch (error) {
            // set the status code and content type
            res.writeHead(404, { "Content-Type": "application/json" });
            // send the error
            res.end(JSON.stringify({ message: error }));
        }
    }

    // /api/todos/ : POST
    else if (req.url === "/api/todos" && req.method === "POST") {
        // get the data sent along
        let todo_data = await getReqData(req);
        // create the todo
        let todo = await new Todo().createTodo(JSON.parse(todo_data));
        // set the status code and content-type
        res.writeHead(200, { "Content-Type": "application/json" });
        //send the todo
        res.end(JSON.stringify(todo));
    }

    // No route present
    else {
        res.writeHead(404, { "Content-Type": "application/json" });
        res.end(JSON.stringify({ message: "Route not found" }));
    }
});

server.listen(PORT, () => {
    console.log(`server started on port: ${PORT}`);
});

测试应用程序

Vanilla Node.js REST API现在已经设置好了,可以测试一下是否一切正常。现在你需要通过运行以下命令来启动服务器。

node app.js

这将在5000端口设置并运行服务器。

A simple server api

使用Postman探索API

让我们使用Postman来测试API中设置的不同方法。

取出所有的todos

/API/todos GET。这将获取data.js 中列出的所有todos。

要测试这个GET请求。

  • 转到Postman并发送一个GET 请求。请求的URL为http://localhost:5000/api/todos ,如下图所示。

Get all todos

  • 这将在Postman的响应部分记录一个响应,其中包括所有的todos,如图所示。data.js

Get all todos

通过id获取一个todo

/API/todos/:id GET。这将只获取一个以todo的id值指定的todo。

要测试这个GET请求。

  • 转到Postman并发送一个GET 请求。输入请求的URL为http://localhost:5000/api/todos/:id ,其中:id 是你要获取的单个todo的id,如下图所示。

Get a single todo

  • 该请求将记录一个单一的todo到Postman响应部分。

Get a single todo

DELETE一个todo

/API/todos/:id DELETE: 这将执行一个单一todo的DELETE 请求。你将只收到一个响应信息,因为数据是临时的,没有存储在数据库中。

为了测试它。

  • 转到Postman并发送一个DELETE 请求。输入请求的URL为http://localhost:5000/api/todos/:id ,其中:id 是你想删除的单个todo的ID,如下图所示。

Delete a todo

  • 这将在你的Postman响应控制台中记录一个Todo deleted successfully 消息。

Delete a todo

更新一个todo

/API/todos/:id PATCH:这将更新一个todo,表示任务已经完成,即truefalse 。你将在postman响应控制台看到效果。

要看看它的效果如何。

  • 转到Postman并发送一个PATCH 请求。输入请求的URL为http://localhost:5000/api/todos/:id ,其中:id 是你要更新的单个todo的ID,如下图所示。

Update a todo

  • Postman的响应控制台应该是这样的。

Update a todo

增加一个新的todo

/API/todos POST。这将创建一个新的todo项目。新的todo将作为一个响应被返回,但它不会被记录在data.js

要对它进行测试,请做以下工作。

  • 转到Postman并打开一个新的标签,选择一个POST 请求并输入请求的URL为http://localhost:5000/api/todos

Add a new todo

转到Body 标签部分,选择raw ,并从右边的下拉选项中选择'JSON'。

Add a new todo

  • 添加新todo的内容(标题、描述和完成)。

下面是一个简单的说明。

{
   "title": "Vannila Node.js REST API",
    "description": "Working with responses and requests",
    "completed": false
}
  • 填写完上述细节后,点击SEND按钮开始POST请求,新添加的todo将被记录在Postman控制台。

Add a new todo

注意:因为ID是随机生成的,所以每次你发出新的POST请求时,它可能会有所不同。

就这样:一个完全由Vannila Node.js编写的简单明了的REST API。我希望你能发现这个教程的指导性、信息性和帮助性。