{"id":262,"date":"2022-05-18T10:48:28","date_gmt":"2022-05-18T08:48:28","guid":{"rendered":"https:\/\/www.davel.fr\/techblog\/?p=262"},"modified":"2022-05-18T10:58:31","modified_gmt":"2022-05-18T08:58:31","slug":"rest-api-after-crud","status":"publish","type":"post","link":"https:\/\/www.davel.fr\/techblog\/2022\/05\/rest-api-after-crud\/","title":{"rendered":"Rest API : After CRUD"},"content":{"rendered":"<p>(originally posted on <a href=\"https:\/\/dev.to\/davel_x\/rest-api-after-crud-406n\">https:\/\/dev.to\/davel_x\/rest-api-after-crud-406n<\/a>)<\/p>\n<p>Hello and welcome to my first &quot;technical&quot; article since a very long time. Last one was on a time when Adobe Flash and Facebook Applications were still a thing. (and maybe you\u2019re too young to know what I\u2019m talking about &#x1f600;)<\/p>\n<h2>Context<\/h2>\n<p>I\u2019ve been developing a REST API recently and I knew quite exactly what to do but I still keep on reading articles about API development to see if there\u2019s new techniques that I don\u2019t know about.<\/p>\n<p>A lot of theses articles are really good to start building a REST API, like these ones for example :<br \/>\n<a href=\"https:\/\/dev.to\/beznet\/build-a-rest-api-with-node-express-mongodb-4ho4\">https:\/\/dev.to\/beznet\/build-a-rest-api-with-node-express-mongodb-4ho4<\/a><\/p>\n<p><a href=\"https:\/\/dev.to\/nditah\/how-to-build-a-rest-api-with-node-prisma-and-postgresql-429a\">https:\/\/dev.to\/nditah\/how-to-build-a-rest-api-with-node-prisma-and-postgresql-429a<\/a><\/p>\n<p>But is it enough to code a CRUD - well more a GPPD (GET-POST-PATCH-DELETE) but let\u2019s stick with readable acronyms &#x1f600; - to put your api in production ?<\/p>\n<p>Well no, not really and I found myself implementing a lot of annex features that are however pretty essential.<\/p>\n<p>As I work with nodejs\/express, I will focus on tools\/code for javascript but the underlying concepts are pretty common.<\/p>\n<p><!--more--><\/p>\n<h2>Who are you? Who, who, who, who?<\/h2>\n<p>Articles about <strong>Authentication<\/strong> are not really rare.<br \/>\nIt's main purpose is to prepare the request for authorization (we'll get there) and\/or filter the endpoints return values.<\/p>\n<p><strong>An example ?<\/strong><br \/>\nImagine you work with several companies that uses the endpoint <code>POST \/users<\/code> to add new users to your services and <code>GET \/users<\/code> to retrieve the users list. You'll want to automatically set a reference to the company on the first endpoint and exclude other companies users on the second.<\/p>\n<p>You could send an api_key to your partners that they would add to the endpoints requests (in the header, the query, etc.) so a simple db request would be enough to know who is interacting with your API.<\/p>\n<p>Here\u2019s a few solutions to get who your API caller is if you need :<\/p>\n<p><strong>External services<\/strong> like Auth0 (<a href=\"https:\/\/auth0.com\/fr\">https:\/\/auth0.com\/fr<\/a>) that also provide authorization services - see below - security protection, etc.<\/p>\n<p><strong>Libs<\/strong> like passportjs (<a href=\"https:\/\/www.passportjs.org\">https:\/\/www.passportjs.org<\/a>) where you can customize as you wish the authentication process (JWT, sessions, apiKeys, oAuth...).<br \/>\nYou\u2019ll find a lot of examples here : <a href=\"https:\/\/dev.to\/search?q=passport\">https:\/\/dev.to\/search?q=passport<\/a><\/p>\n<p><strong>DIY<\/strong> with the solution that you prefer.<br \/>\nFor example with JWT : <a href=\"https:\/\/dev.to\/perrydbucs\/using-jwts-for-authentication-in-restful-applications-55hc\">https:\/\/dev.to\/perrydbucs\/using-jwts-for-authentication-in-restful-applications-55hc<\/a><\/p>\n<p>For my API I wanted to use APIKey in http bearer AND a JWT in HTTPOnly cookie so I choosed passportjs that is common, reliable and has a lot of features.<\/p>\n<h2>You don't own me (I'm not just one of your many toys)<\/h2>\n<p>Ok, now you know who is calling (the &quot;user&quot;) you may have the need to restrict some endpoints : does the user can see the resource, can interact with the resource or not ? That's <strong>Authorization<\/strong>.<\/p>\n<p><strong>An example ?<\/strong><\/p>\n<p>&quot;Umbrella Corporation&quot; calls the endpoint <code>POST \/virus<\/code> with it's API_Key. Alas, this endpoint is only accessible by Admin Users and Umbrella is not one of them. The endpoint should return response with the HTTP error code 403 (<a href=\"https:\/\/en.wikipedia.org\/wiki\/HTTP_403\">https:\/\/en.wikipedia.org\/wiki\/HTTP_403<\/a>).<\/p>\n<p>Some of the ways to discriminate users are :<\/p>\n<p><strong>RBAC<\/strong> just add a role in the User entity of your base and checks on each request if the current user has the correct role(s) to access the resource. (I know my explanation is simplified)<\/p>\n<p><strong>ACL<\/strong> More granular than RBAC, this time each object has a list of users\/roles\/groups with the action authorized for each one of them. Thats very powerful but often really painful to manage on small to medium projects.<\/p>\n<p><strong>ABAC<\/strong> Permissions are based on attributes (you choose) of the object to check. <code>creator<\/code>, <code>status<\/code>, whatever you want can be used to make rules to verify if the user has permissions.<\/p>\n<p>Here are some tools that can help you handle authorization with nodejs :<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/stalniy\/casl\">https:\/\/github.com\/stalniy\/casl<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/tschaub\/authorized\">https:\/\/github.com\/tschaub\/authorized<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/onury\/accesscontrol\">https:\/\/github.com\/onury\/accesscontrol<\/a><\/li>\n<\/ul>\n<p>My advice on that point : start with the simplest way and change if it's really getting difficult to implements auth rules.<\/p>\n<p>For my project I coded a simple solution inspired by Symfony's Voters :<br \/>\nI created a class for each typeof object (usually models) that needs permissions with the same static method <code>check()<\/code>. (yeah, use an Interface in any other language &#x1f601;)<\/p>\n<pre><code class=\"language-javascript\">class UserPermissions {\n  \/**\n   * Checks if {user} can {verb} the {object} (or just {verb} is object is null)\n   *\n   * @params {User} instance of the user to check permissions\n   * @params {string} permission name\n   * @params {Object|null} object\n   *\n   * @returns {Boolean} has permission ?\n   *\/\n  static check(user, verb, object = null) {\n    if (!user || !(object instanceof User)) return false;\n    switch (verb) {\n      case &quot;read&quot;:\n        return UserPermissions.canRead(user, object);\n      case &quot;write&quot;:\n        return UserPermissions.canWrite(user, object);\n      default:\n        return false;\n    }\n  }\n\n  \/\/ only admins and user him.her.self  can write (edit) a user\n  static canWrite(user, object) {\n    return user.role === &quot;admin&quot; || String(user.id) === String(object.id);\n  }\n\n  \/\/ only admins and user him.her.self can read (view) a user\n  static canRead(user, object) {\n    return user.role === &quot;admin&quot; || String(user.id) === String(object.id);\n  }\n}\n\nexport default UserPermissions;<\/code><\/pre>\n<p>And there's a kind of factory to get the proper auth class depending on the object.<\/p>\n<pre><code class=\"language-javascript\">\/**\n * Returns an auth checker class relater to an object type\n *\/\nconst getAuthClass = (object) =&gt; {\n  if (object instanceof User) {\n    return UserPermissions;\n  }\n  \/\/\/ ... other ones\n  return false;\n};\n\nexport const checkAuthorisation = async (user, verb, object) =&gt; {\n  const authClass = getAuthClass(object);\n  return authClass ? await authClass.check(user, verb, object) : false;\n};\n\nexport default { checkAuthorisation };<\/code><\/pre>\n<p>and then a simple usage :<\/p>\n<pre><code class=\"language-javascript\">await checkAuthorisation(UmbrellaCorpUser, &quot;read&quot;, JillValentineUser);\n\/\/ returns false obviously<\/code><\/pre>\n<p>I also created a middleware (a previous middleware gets the object to test and stores it in <code>req.element<\/code>).<\/p>\n<pre><code class=\"language-javascript\">const PermissionMiddleware = {\n  \/\/\n  can(verb, permissionClass) {\n    return (req, res, next) =&gt; {\n      if (permissionClass.check(req.user, verb, req.element)) {\n        return next();\n      }\n\n      return next(new Error(&quot;Unauthorized&quot;)); \/\/ use httperrors\n    };\n  },\n};\n\nexport default PermissionMiddleware;\n\n\/\/\n\nimport Machin from &quot;models\/Machin&quot;;\n\nrouter.get(\n  &quot;\/machin\/:id&quot;,\n  GetElementById(Machin), \/\/ the fetcher middleware\n  PermissionMiddleware.can(&quot;action&quot;, MachinPermissions),\n  function (req, res, next) {\n    res.json({ title: &quot;debug test&quot; });\n  }\n);<\/code><\/pre>\n<p>It fits my usage for the moment but maybe using a more common lib is more easy, more tested, more documented.<\/p>\n<h2>Can't take my eyes off of you<\/h2>\n<p>Here I wanted to write a very long explanation about audit logs but I found this article during the making of that is really explanatory :<br \/>\n<a href=\"https:\/\/dev.to\/chipd\/making-audit-logs-sexy-best-practices-for-audit-logging-with-examples-1pab\">https:\/\/dev.to\/chipd\/making-audit-logs-sexy-best-practices-for-audit-logging-with-examples-1pab<\/a><\/p>\n<p>I'll try to make it shorter : you, or your clients but principally you, want to know what happens with the modifying enpoints (POST, DELETE, PATCH) of your API : what whas send, when and what was the result.<\/p>\n<p>If there's an error you wand to know why and reproduce. If there was a unexpected change in a DB entry you whan to know who did it and when (and ask this person why... with or without knives involved, that's your problem).<\/p>\n<p>Here's my technique to do that, first I create an Express Middleware that will catch the <code>res.end()<\/code> and log the request before calling it manually<\/p>\n<pre><code class=\"language-javascript\">function apiLoggerMiddleware(req, res, next) {\n  const oldWrite = res.write;\n  const oldEnd = res.end;\n\n  const chunks = []; \/\/ this will store everithing written in the res\n\n  res.write = (...restArgs) =&gt; {\n    chunks.push(Buffer.from(restArgs[0]));\n    oldWrite.apply(res, restArgs);\n  };\n\n  res.end = async (...restArgs) =&gt; {\n    if (restArgs[0]) {\n      chunks.push(Buffer.from(restArgs[0]));\n    }\n    const body = Buffer.concat(chunks).toString(&quot;utf8&quot;);\n\n    \/\/ APILog is an Mongoose Model but you can use whathever you want, Postgres, event Excel, I don&#039;t care. :)\n    const apilog = new APILog({\n      time: new Date(),\n      headers: {\n        ...req.headers, \/\/ beware of headers, sometimes you want to remove some elements\n        remoteAddress: req.connection.remoteAddress,\n        cookie: undefined, \/\/ don&#039;t store cookies, you shouldn&#039;t care user&#039;s JWT\n      },\n      method: req.method,\n      originalUri: req.originalUrl,\n      uri: req.url,\n      path: req.url.split(&quot;?&quot;).shift(),\n      requestUser: req.user, \/\/ specific to passportjs\n      requestData: req.body, \/\/ here&#039;s the user input\n      responseData: body, \/\/ here&#039;s your route response\n      responseStatus: res.statusCode, \/\/ here&#039;s your route response status\n    });\n    await apilog.save();\n\n    oldEnd.apply(res, restArgs);\n  };\n\n  next();\n}\n\nexport default apiLoggerMiddleware;<\/code><\/pre>\n<p>Beware of something with this way of logging data : if you're in Europe or work with Europe you've got to be GDPR compliant. I really suggest to get in touch with a Data Protection Officer (DPO) to implement that.<\/p>\n<p>Let's use this middleware with a (very simplified) route now :<\/p>\n<pre><code class=\"language-javascript\">const router = Router();\n\n\/**\n * GET \/bundles\n *\/\nrouter.post(&quot;\/milk-shake&quot;, apiLoggerMiddleware, async (req, res, next) =&gt; {\n  try {\n    const isBoysToTheYard = await isBetterThanYours(req.user); \/\/ don&#039;t mind about theses fake functions\n    const milkShake = await createMilkShake(req.body, isBoysToTheYard);\n    res.json({ milkShake: formatMilkShake(milkShake) });\n  } catch (error) {\n    next(ErrorsHandler(error)); \/\/ I haven&#039;t talked about error handling... maybe another day\n  }\n});<\/code><\/pre>\n<h2>You got to keep me focused, you want it, say so<\/h2>\n<p>Ok, your API is ready to rumble, and users will start to use it... but how ? I'm sure you don't want to share your source code and even less spen an hour of visio conference with each user to explain every endpoint.<\/p>\n<p>It seems pretty obvious but yes you'll have to write a document that explains how to use you API in details : that's <strong>Documentation<\/strong>.<\/p>\n<p>I won't lie : it takes a looot of time to write down useful documentation. You've got to be as exhaustive as possible, but simple enough to be understable quickly and give examples of course.<\/p>\n<p>I will focus here on two points, documenting the API and documenting the whole usage of your app.<\/p>\n<p>For the API documentation here are some tools :<\/p>\n<p><strong>APIDoc<\/strong> : Just add annotations to your routes and voil\u00e0, you can generate a documentation with a simple command-line.<\/p>\n<p>I tried it and what I liked is the use of annotations to describe your API, but I left it because I didn't like the documentation template and didn't found any other one that fited my needs (there's not a lot of them).<\/p>\n<p>You can find it here : <a href=\"https:\/\/apidocjs.com\/\">https:\/\/apidocjs.com\/<\/a><\/p>\n<p><strong>OpenAPI<\/strong> (formerly swagger) is a specification to describe your API so it can be understood by humans or automated scripts.<br \/>\nYou can find it here : <a href=\"https:\/\/swagger.io\/specification\/\">https:\/\/swagger.io\/specification\/<\/a><\/p>\n<p>I use it on my Express Application with the plugin <a href=\"https:\/\/www.npmjs.com\/package\/@wesleytodd\/openapi\">@wesleytodd\/openapi<\/a> that offers a middleware usable for each route to describe them - it can even validate input data.<\/p>\n<p>When your specification is ready you can use a compatible UI to have a nice and useful presentation of your work. The <a href=\"https:\/\/petstore.swagger.io\/\">Swagger UI<\/a> is well known and very clear but I like the <a href=\"https:\/\/redocly.github.io\/redoc\/\">Redoc<\/a> one. I guess it's a question of taste too. &#x1f601;<\/p>\n<p><strong>APIBlueprint<\/strong> I stumbled upon this specification recently because of the Chappe documentation engine (see below) and I didn't try it but it seems interesting.<\/p>\n<p>You can find it here : <a href=\"https:\/\/apiblueprint.org\/\">https:\/\/apiblueprint.org\/<\/a><\/p>\n<h3>What's up doc ?<\/h3>\n<p>That's nice you've got a gread documentation on your API endpoints but maybe there is more about your application to explain, give a glimpse of the workflows, schemas, and other stuff. There comes the usage for documentations engines. Here's a few of them.<\/p>\n<p><strong>Chappe<\/strong> Created by the company behind the <a href=\"https:\/\/crisp.chat\/en\/\">Crisp<\/a> messaging platform for <a href=\"https:\/\/docs.crisp.chat\/\">their own documentation<\/a> it has the advantage of havint the possibility to integrate a APIBlueprint specification document to generate a nice interface to browse your endpoints. Alas, no OpenAPI plugin seems available at the moment... since the project is open-source maybe in the future ?...<\/p>\n<p>You can find it here : <a href=\"https:\/\/github.com\/crisp-oss\/chappe\">https:\/\/github.com\/crisp-oss\/chappe<\/a><\/p>\n<p><strong>Docusaurus<\/strong> from the open source Meta projects, this one helps you generate a static React documentation with plenty of features like i18n, versionning, etc.<\/p>\n<p>You can find it here : <a href=\"https:\/\/docusaurus.io\/fr\/\">https:\/\/docusaurus.io\/fr\/<\/a><\/p>\n<p>If you know more tools like theses, or if you have tried some and have a constructive point of view, feel free to share in the comments. :)<\/p>\n<h2>This is the end, my friends<\/h2>\n<p>Hmmm, not really I'm sur there is plenty of things to do, but this is the end of my article. I hope you enjoyed it.<\/p>\n<p>I just realized that I could have separated it to make a series but I guess I'm lazy and I've got some Kreple'h to finish cooking.<\/p>\n<p>Cheers ! &#x1f917;&#xfe0f;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(originally posted on https:\/\/dev.to\/davel_x\/rest-api-after-crud-406n) Hello and welcome to my first &quot;technical&quot; article since a very long time. Last one was on a time when Adobe Flash and Facebook Applications were still a thing. (and maybe you\u2019re too young to know what I\u2019m talking about &#x1f600;) Context I\u2019ve been developing a REST API recently and I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-262","post","type-post","status-publish","format-standard","hentry","category-divers"],"_links":{"self":[{"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/posts\/262","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/comments?post=262"}],"version-history":[{"count":2,"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/posts\/262\/revisions"}],"predecessor-version":[{"id":265,"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/posts\/262\/revisions\/265"}],"wp:attachment":[{"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/media?parent=262"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/categories?post=262"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.davel.fr\/techblog\/wp-json\/wp\/v2\/tags?post=262"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}