Writing in the front

Again want to write a series of articles, was up so a title is totally from my own practice, my personal feeling is that you learn a new knowledge, especially the technical, if just speaking like a book, from the beginning to the end of the rolled over the documents, after will not reach the application level, in trying or constant interruption, and then go back to see, I don’t think it’s very efficient. So, what I usually do is go straight to the front end, after all, the front-end technology is pretty much the same. Framework syntax + routing + some of their own constraints, just run a demo feel almost ready to start. As for the deeper things, you can write when you encounter problems and then go deep, so that knowledge points are more firmly mastered.

Of course, everyone’s habits are different, so I’m just putting a gimmick on this series of articles, haha 😄.

What is a apiDoc

Let me flatter myself, I have read some apIDOC articles, personally feel that this article as an entry level should be the most detailed, whether the structure of the article or sample code, I hope friends patiently read 😄

Normal operation, although I said that I like to learn while writing, but first of all to open the official website to see the basic knowledge, at least you have to know what this thing is doing, based on what, what norms and so on. ApiDoc introduces Inline Documentation for RESTful Web APIs. The translation is “Providing RESTful Web APIs in the form of inline documents.” The official website is also not much to say, up directly is a variety of Demo, because it is the front end, I only care about JavaScript:

Apidoc supports many languages, such as Java, PHP, Python and JavaScript.

/**
 * @api {get} /user/:id Request User information
 * @apiName GetUser
 * @apiGroup User
 *
 * @apiParam {Number} id Users unique ID.
 *
 * @apiSuccess {String} firstname Firstname of the User.
 * @apiSuccess {String} lastname  Lastname of the User.
 */
Copy the code

As you can see, ApidOC helps us generate RESTful Web APIs by inserting standard annotations into the code and then parsing them by running corresponding commands. This way also means that there are many constraints, we must strictly follow the constraints, of course, there are some advantages, but no mistakes, strong convention means strong standardization.

The basic annotation specification of APidOC

As I said, the stronger the constraint, the easier it is to write, because there is nothing creative, and the constraints of apidoc are all @apiParam.

As shown in the picture above, it’s not as much as you might think, as long as we get all of these, it’s basically OK.

Write and learn

Since it is learning while writing, the difference with other articles is that, instead of translating every @ API bit by bit according to official documents, it is directly used and understood one by one. It is much more efficient to understand from examples. So WHAT I’m going to do is write a simple demo first, and then work on it, and then go through all the apiParam, and this article will be over, with some sample code, and you’ll be comfortable with it. Learn to have code is also simple ~

Step 1 – Install apidOC

If you want to use it, you have to install it first.

NPM install --save -- dev apidoc install -- dev apidocCopy the code

Step 2 – Configure the relevant files

Since apidoc finally gives us a static file that we can access, we can configure this service through apidoc.json.

There is a premise that you have to have a project of your own. I created a service directly through Node, a very simple small project for this article Demo.apidoc-demo, like can give a 🌟

// apidoc.json
{
  "name": "apidoc-demo"."version": "1.0.0"."description": "Learning by Writing series -- APidoc"."title": "apidoc-demo"."url" : "http://localhost:3333"."preview-url": "http://localhost:3333/apidoc/index.html"// Preview service address}Copy the code

Name, version, and description are basic configuration fields. Others can be configured according to your own convenience.

Step 3 – First example

OK, the exciting moment is coming. The excitement of learning by writing is that you don’t really understand how this thing works and how it works. You can write it successfully by using a Demo, and then you just need to learn more about it

@routes /users.js /** * @api {get} /users * @apidescription * @apisuccessexample {json} Success-response: * HTTP/1.1 200 OK * {*"errcode": *, 0"message": "",
 *      "data": [{*"name" : "userName",
 *          "email" : "userEmail"* * *}}] * @ apiSampleRequest http://localhost:3333/users @ apiVersion 1.0.0 * / router. Get ('/'.function(req, res, next) {
  res.json({
    errcode: 0,
    message: ' ',
    data: [
      {
        name: 'luffy',
        email: '[email protected]'
      }, {
        name: 'naruto',
        email: '[email protected]'}}]); });Copy the code

Above we wrote an API to get a list of users, and then we generated the API documentation.

There are two preconditions to note here

  • The first one, we’re inpulic/Creating a Directoryapidocfolder
  • Second, we runapidoc -i routes/ -o public/apidoc/The command

I routes/ -o public/apidoc/ apidoc I routes/ -o public/apidoc/ And then you output it to the public/apidoc/ directory, which is really arbitrary, you can output it anywhere, because its output is just a set of static files, styled HTML. So since it is node service, MY static server is public, I put the generated file in the public/apidoc/ folder, the project starts actually the service can also be accessed, very convenient. So the above apidoc json I wrote the preview – the url: http://localhost:3333/apidoc/index.html.

Since the command is long, it is convenient to encapsulate it later:

// package.json
...
"scripts": {
    "start": "DEBUG=apidoc-demo:* nodemon ./bin/www",
+   "apidoc": "apidoc -i routes/ -o public/apidoc/"}...Copy the code

Run yarn APidoc. The console displays the following output indicating that the operation is complete

The /punlic/apidoc/ directory also contains the content generated by apidoc for us:

Then we can start the service yarn start, go to http://localhost:3333/apidoc/index.html.

  • @api – Definition This is an API of apidoc

    '@api {method} path [title]' Required, this is mandatory, each APidOC API document must have this ziduanCopy the code
  • ApiDescription – Description of this API

    Usage: @apidescription text describes what this API doesCopy the code
  • @apisuccessexample – Success example

    Usage: @apisuccessexample [{type}] [title] example Example of a successful responseCopy the code
  • @apisAmplerequest – Request address

    Usage: @apisAmplerequest URL is used to click the address that sent the sample requestCopy the code
  • @apiversion-API version number

    Usage: @apiVersion Version current API versionCopy the code

Step 4 – Advanced learning

The first example above is a successful run, and you should have a few basic field meanings and how to use them. Let’s take it a step further and extend the API parameters to enrich our documentation. First let’s see what a bunch of stuff is. It looks really ugly…

Extended functionality – grouping + response parameters

/routes/users.js provides an interface for the User, and according to the previous interface specification, the interface should also be grouped. So here we are, extending the interface — grouping.

  • Group – @ apiGroup
    /** * @api {get} /users * @apidescription obtain User list + * @apigroup User * @apisuccessexample {json} Success-Response: * HTTP/1.1 200 OK * {*"errcode": *, 0"message": "",
      *      "data": [{*"name" : "userName",
      *          "email" : "userEmail"* * *}}] * @ apiSampleRequest http://localhost:3333/users @ apiVersion 1.0.0 * /Copy the code

At the same time, we can see above, when the success of the return, but the returned data description does not, continue to extend, return data format and description of the extension interface function – response data specification

  • Response – @ apiSuccess
    /** * @api {get} /users * @apidescription GetUser list * @apiname GetUserList * @apigroup User + * @apisuccess {array} data Response data + * @apisuccess {string} message Response message + * @apisuccess {number} errCode Error code * @apisuccessexample {json} success-Response: * HTTP/1.1 200 OK * {*"errcode": *, 0"message": "",
      *      "data": [{*"name" : "userName",
      *          "email" : "userEmail"* * *}}] * @ apiSampleRequest http://localhost:3333/users @ apiVersion 1.0.0 * /Copy the code

Yarn apidoc && yarn start

As you can see, all of our extensions are working as expected. For more extensive use of both, such as default-value for response parameters, check out the official website

Extended features – Error response + carry parameters

As you can see from the example above, it is close to a complete API documentation with two extensions, but since successExample, errorExample should also be available, since not all requests will succeed, and there are also failed responses.

The first interface is to GET a list of all users, so it is OK to GET /users directly, then the problem is, if there is a parameter, how to define the parameter? So let’s write an interface with parameters, and let’s continue to refine the functionality.

/** * @api {get} /users/:id GetUserInfoById * @apidescription Gets the User list * @apiname GetUserById * @apigroup User * @apiParam {Number} ID Users unique ID. * @apisuccess {Object} data Response data * @apisuccess {String} message Response message * @apisuccess {Number} errCode Error code (self-defined, 0 indicates no error) * @apisuccessexample {Json} success-Response: * HTTP/1.1 200 OK * {*"errcode": *, 0"message": "",
 *      "data": {*"id": *, 0"name" : "userName",
 *          "email" : "userEmail"* } * } * @apiError {4XX} UserNotFound The <code>id</code> of the User was not found. * @apiErrorExample {json} Error-response: * HTTP/1.1 404 Not Found * {*"error": "UserNotFound"*} * * @ @ apiSampleRequest http://localhost:3333/users/:id apiVersion 1.0.0 * / router. Get ('/:id'.function(req, res, next) {
  const { id } = req.params;
  if(! Userdata.some (item => item.id === parseInt(id, 10)) {// No direct 404 existsreturn res.status(404).json({
      errcode: 404,
      message: `The ${id}of users was not found! `, data: {} }); } // res.json({errcode: 0, message:' ',
    data: userData.find(item => item.id === parseInt(id, 10))
  })
});

Copy the code

The code is a bit long, but most of it is made up of comments to apidoc. As you can see, two additional annotations, @apiParam and @apierRorexample, are also quite simple, one for parameters and one for error response examples.

  • @ apiParam – parameters

    Usage: @apiparam [(group)] [{type}] [field=defaultValue] [description] Request parameters, type, can include default values and parameter descriptionCopy the code
  • @APIError – Defines error messages

    @apiError [(group)] [{type}] field [description] defines error types, such as {4XX} error, {5XX} errorCopy the code
  • @APIerRorexample – Error response example

    Usage: @apierrorexample [{type}] [title] example Example of an error responseCopy the code

Let’s look at this in action:

We send an Error request, because 3 is not found, so 404 Error is returned. The successful request is as follows:

Advanced processing – @apiParam can be used as either a param or a query

This is a little tip, but it’s also something you experience when you step on a pit. I was wondering why there was only @apiParam in the annotation and not @apiQuery, because there are many query apis in the actual scenario, but I honestly didn’t find them, although RESTful apis can be put in param, but STILL confused. (It would be appreciated if a bull could explain it to me)

// @apiParam -> @apiQuery (); // @apiparam -> @apiQuery (); // @apiparam -> @apiQuery (); /** * @api {get} /users/:id GetUserInfoById * @apidescription Get user list * @apiParam {Number} id *... * * @ @ apiSampleRequest http://localhost:3333/users apiVersion 1.0.0 * /Copy the code

We’re up here;

The original: @ apiSampleRequest: http://localhost:3333/users/:id to = > @ apiSampleRequest: http://localhost:3333/users and also can produce a param is id field, we fill in in id, in the background will be the param converted to the form of a queryCopy the code

[Note] : The official documentation does not say so, but I found consistent performance when I used it.

Extended functions – @apideFine pulls out public blocks + @apiuse uses public blocks

So far, annotations are almost ready to use, but there is a problem. If you want to write full, the comments at the top of each API are super long. This makes use of the extension functions – @apidefine and @apiuse

  • ApiDefine – Define code blocks
    Usage: @apidefine Name [title] [description] defines a common code block, which can then be used by @apiUseCopy the code
  • ApiUse – Uses a predefined code block
    Usage: @apiUse Name Uses a code block defined by @apideFineCopy the code

For example, if you can pull off the above part, you can obviously pull off the success return field, because success always returns two fields, errcode,message, and data, so you don’t pull off the success return field because of the uncertainty of the content type. So pull out a block of code that returns a field on success and use it.

* @apisuccess {Number} errcode The success res code. * @apisuccess {Strng} /** * @api {get} /users GetUserList * @apidescription GetUserList * @apiname GetUserList * @apigroup User + * @apiUse CommonSuccess * @apisuccess {Array} data Response data * @apisuccessexample {json} Success-response: * HTTP/1.1 200 OK * {*"errcode": *, 0"message": "",
     *      "data": [{*"id": *, 0"name" : "userName",
     *          "email" : "userEmail"* * *}}] * @ apiSampleRequest http://localhost:3333/users @ apiVersion 1.0.0 * /Copy the code

Other Notes

In addition to the above annotations, there are some other annotations that may be used, which are also mentioned here in passing.

  • @APIHeader – Header field (similar to param, but placed in the header of req)
    @apiHeader [(group)] [{type}] [field=defaultValue] [description] is placed in the header of req, usually used for verification, such as JWTCopy the code

For example, I require an authorization field in the header of the interface that gets a list of all users.

/** * @api {get} /users GetUserList * @apidescription GetUser list * @apiname GetUserList * @apigroup User + * @apiheader {String} Authorization User Authorization code * @APIUse CommonSuccess * @APisuccess {Array} data Response data * @APisuccessexample {json} Success-response: * HTTP/1.1 200 OK * {*"errcode": *, 0"message": "",
  *      "data": [{*"id": *, 0"name" : "userName",
  *          "email" : "userEmail"* *}}] + * @ apiUse InvalidToken * * @ @ apiSampleRequest http://localhost:3333/users apiVersion 1.0.0 * /Copy the code

As shown in the figure, the token and header are asked to be put in while the request is being simulated.

  • @apiPermission – API with permissions
    For example, some apis require administrator access, or require header anthorization, etc.Copy the code

Also, let’s get the user list to add permission hint:

/** * @apidefine token /** * @apidefine token /** * @apidefine token Use @apiPermission @apiPermission tokenCopy the code

  • @APIIgnore – Ignore some API, such as some unfinished methods
    This annotation is used when a method is incomplete and does not want to be exposedCopy the code

Let’s just write a new API in our code and tag it @apiignore:

/** * @apiignore POST USER * @api {POST} /users */ router. POST ('/users'.function (req, res, next) {
    console.log('Unfinished POST USER');
  });
Copy the code

As you can see, our document still only has two apis, and the POST API is indeed ignored.

Pull out the common code block

This is not a comment, which I think is worth mentioning in the project specification, which is that the code blocks we have isolated can be managed uniformly, rather than individually for each file. I put it in /routes/apidoc/common.js. Then other routing files can be used normally. It looks more standard on the whole project. Depending on your needs, when the project is large, you can also package the response separately

Extension – vscode-apidoc-snippets

conclusion

Finally, I want to emphasize that I created a Node service for the purpose of writing this article. In fact, this service is not suitable, or this scenario is not suitable. Why? Because of the node+ rendering engine’s development mode, the front-end and back-end are written by one person, and the interface and page coupling is very serious, one person to write actually may not need API documentation or scene is not very appropriate. I think it is very suitable for the separation of the front and back end, node end as the back end, although it may be a person to write but the separation may be more thorough ~

The first article of this series was relatively smooth. The most important thing is that I did learn apidOC while writing code, and I learned the trinity feeling deeply. Is not so much an article, more is a record process, but this process I think can let a lot of white less take a lot of detours, at least have a complete example code, a detailed learning process, step by step, or more suitable for novice ~

Apidoc-demo code address, you see the officer, any comments can be raised, hope to pay more attention to more like.