{"id":16721131,"url":"https://github.com/khalyomede/jur","last_synced_at":"2025-03-15T12:23:18.447Z","repository":{"id":96870149,"uuid":"98657896","full_name":"khalyomede/jur","owner":"khalyomede","description":"JSON Uniform Response","archived":false,"fork":false,"pushed_at":"2018-06-23T12:48:29.000Z","size":18,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-15T03:47:38.067Z","etag":null,"topics":["api","api-rest","json","json-api","response","rest","rest-api","standard","uniform"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/khalyomede.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-28T14:33:25.000Z","updated_at":"2018-06-23T12:48:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"a5f2ef5c-5374-4f1c-b406-3484cd188edf","html_url":"https://github.com/khalyomede/jur","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khalyomede%2Fjur","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khalyomede%2Fjur/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khalyomede%2Fjur/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/khalyomede%2Fjur/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/khalyomede","download_url":"https://codeload.github.com/khalyomede/jur/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243727251,"owners_count":20337956,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-rest","json","json-api","response","rest","rest-api","standard","uniform"],"created_at":"2024-10-12T22:28:57.428Z","updated_at":"2025-03-15T12:23:18.421Z","avatar_url":"https://github.com/khalyomede.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# JUR\n\nJSON Uniform Response\n\n[![GitHub tag](https://img.shields.io/github/tag/khalyomede/jur.svg)]()\n[![Reading time](https://img.shields.io/badge/read%20time-6%20mins-lightgrey.svg)]()\n\n## Summary\n\n- [What is JUR](#what-is-jur)\n- [Standard](#standard)\n- [Real life example](#real-life-example)\n- [Tips](#tips)\n- [Attributes cheat sheet](#attributes-cheat-sheet)\n- [List of libraries that implement JUR](#list-of-libraries-that-implement-jur)\n\nYou are watching version 2, which supersedes the previous version. Version 1 is no longer maintened (you can still access it through the tag menu right above).\n\n## What is JUR\n\nJson Uniform Response is my attempt at making the job of developers that consumes API easier by a set of defined rules to make the data retrieving task the same regardless of the nature of the data we are trying to fetch or alter. This might seems a little bit abstract said like this, so quickly jump into the [real life example](#real-life-example) if you need some real use cases.\n\nJUR is optimized for REST API that respond with JSON data.\n\n## Standard\n\n- [Overview](#overview)\n- [In-depth attributes explaination](#in-depth-attributes-explaination)\n\n### Overview\n\nNo matter which endpoint you request, nor which type of HTTP request you make, this is what you will always have in response:\n\n```json\nGET http://example.com/api/task\n\n{\n  \"message\": null,\n  \"request\": \"get\",\n  \"data\": [\n    {\n      \"id\": 1,\n      \"name\": \"Fix the :hover state of the submit button\",\n      \"updated_at\": \"2018-06-20T10:07:37+02:00\"\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Add Youtube social icon on the menu\",\n      \"updated_at\": \"2018-06-20T11:29:01+02:00\"\n    }\n  ],\n  \"debug\": {\n    \"elapsed\": 120000,\n    \"issued_at\": 1529843640000000,\n    \"resolved_at\": 1529843640120000\n  },\n}\n```\n\n### In-depth attributes overview\n\n- [message](#message)\n- [request](#request)\n- [data](#data)\n- [debug](#debug)\n\n#### message\n\nThis is the right place to inform the end user of what is happening with his request. \n\nThe user can be a developer, but also a non-developer like a customer in a shop website. So this message should be targeted to offer a good comprehension, **without** exposing sensible information about the server or any other critical data.\n\n#### request\n\nThis is the attribute that saves the type of request that have been processed. This attribute can help developers in 2 ways:\n\n- verify if its request have been understood by the server (if the developer intended to make a GET request, but the server ended up processing a PUT request, there is an issue)\n- help the front-end developer process the message regarding the type of request (styling, ...)\n\n#### data\n\nThis is the attribute where all the necessary data to provide are stored. It can be set to null when there is no data to return, which is different than the absence of result (which would be an empty array/object).\n\n#### debug\n\nThis is the attribute designed for the consumer of the API. It let you know when the request has been issued and resolved according to the server, and how many time did the server took to resolve this request.\n\n**elapsed**\n\nThe time elapsed by the controller to resolved the request. In **microseconds**.\n\n**issued_at**\n\nThe time at which the request has been handled by the controller. Timestamp in **microseconds**.\n\n**resolved_at**\n\nThe time at which the request has been resolved by the controller. Timestamp in **microseconds**.\n\n## Real life example\n\nThis example features:\n\n- khalyomede/jur-js: for the step of sending the request/consuming the response\n- Laravel (PHP): for the step of processing the request\n- khalyomede/jur (PHP): the package that help form the JUR response\n\nWe are in the context of a task application, and the user is displaying the task list.\n\n- [1. Sending the request/consuming the response](1-sending-the-requestconsuming-the-response)\n- [2. Processing a request](2-processing-a-request)\n\n### 1. Sending the request/consuming the response\n\n```html\n\u003cscript type=\"text/javascript\"\u003e\n  document.addEventListener('DOMContentLoaded', function() {   \n    fetch('/api/task', { method: 'GET' }).then(function(response) {\n      if( resp.ok ) {\n        let response = new Jur().parse(response.json());\n\n        console.log(response.data());\n      }\n    });\n  });\n\u003c/script\u003e\n```\n\n### 2. Processing a request\n\n```php\nnamespace App\\Http\\Controllers;\n\nuse App\\Task;\n\nclass TaskController extends Controller {\n  public function index(): string {\n    return response()-\u003ejson( jur()-\u003edata( Task::all() )-\u003etoArray() );\n  }\n}\n```\n\n## Tips\n\n- [Check for errors](#check-for-errors)\n- [Use the debug information to monitor latency](#use-the-debug-information-to-monitor-latency)\n- [Version your routes](#version-your-routes)\n- [Internationalize your routes](#internationalize-your-routes)\n\n### Check for errors\n\nIf you have seen the version 1, you will notice there is no `status` (`success`, `fail` or `error`).\n\nYou can now check for errors using the correct manner, which is to check on the HTTP status code of the request.\n\nHere is an example of a simple error management using javascript:\n\n```javascript\nfetch('/api/task', { method: 'POST', body: JSON.stringify({name: 'Do the dishes'}) }).then(function(response) {\n  if( ! response.ok ) {\n    handleErrors(response.status, response.json().message);\n  }\n\n  return response;\n}).then(function(response) {\n  // process the response as usual\n});\n```\n\nAnd in our `handleErrors` function:\n\n```javascript\nfunction handleErrors(code, message) {\n  switch(code) {\n    case 400:\n      // display an alert in orange\n    case 500:\n      // display an error in red\n    // ...\n  }\n}\n```\n\n### Use the debug information to monitor latency\n\nOne interesting thing about the debug attribute is that you can use it to compute latency between the time your client sent the request until the moment the controller is about to start resolving the request. \n\n_Keep in mind the request could also go through many layers like framework middlewares, CDNs, ... All of these should be taken into an account._\n\nSo for example in Javascript we could compute this like this:\n\n```javascript\nconst client_issued_at = Date.now() + new Date().getMilliseconds();\n\nfetch('/api/task', { method: 'POST', body: JSON.stringify({ name: 'rework JUR standard' }) }).then(function(response) {\n  const resp = response.json();\n  const sever_issued_at = resp.debug.issued_at;\n  const latency = server_issued_at - client_issued_at;\n\n  alert(resp.message + '. Done in ' + Math.round(resp.debug.elapsed / 1000) + 'ms (+ ' + Math.round(latency) + 'ms).');\n});\n```\n\nWhich would return to the client an alert with the following message:\n\n```\nTask saved. Done in 120ms (+ 90ms).\n```\n\n**Note**\n\nYou can also use existing third-party library that implements such behavior, like [khalyomede/jur-js](https://github.com/khalyomede/jur-js), which make this job easier:\n\n```javascript\nvar jur = new Jur().issued();\n\nfetch('/api/task', { method: 'POST', body: JSON.stringify({ name: 'rework JUR standard' }) }).then(function(response) {\n  jur.parse(response.json());\n\n  alert(jur.message() + ' Done in ' + jur.elapsed('millisecond') + 'ms (+' + jur.latency('millisecond') + 'ms).');\n});\n```\n\nWhich will display:\n\n```\nTask saved. Done in 120ms (+90ms).\n```\n\n### Version your routes\n\nIn case of a breaking change in your tables, you might want to version your API to not force all your front-end developper to immediately change their code. Simply add a `v1`, to your routes.\n\n### Internationalize your routes\n\nAs the message attribute is intended for being displayed to the end user, you will also want to personalize the message regarding the locale of the device. For this, I propose you to add the locale in the route like:\n\n- `/api/v1/en-us/task`\n- `/api/v1/fr-fr/configuration/notification`\n- ...\n\nSpecifying the locale in the route instead of on the header or the body/query parameters has the advantage of making this a requirement more than an option, so it is allow the back-end developer to have less verification to do or remove some tasks (like having to split by `;` the language of the `Accept-Language` header, in case there is multiple language, or to interpret `*` and having to fetch the default language, ...).\n\n## Attributes cheat-sheet\n\n| attribute   | parent | type        | format      | example                                                                             |\n|-------------|--------|-------------|-------------|-------------------------------------------------------------------------------------|\n| message     |        | string,null |             | \"Task created successfuly.\"                                                         |\n|             |        |             |             | null                                                                                |\n| request     |        | string      |             | get                                                                                 |\n|             |        |             |             | post                                                                                |\n|             |        |             |             | put                                                                                 |\n|             |        |             |             | patch                                                                               |\n|             |        |             |             | delete                                                                              |\n| data        |        | *           |             | [{\"id\": 1, \"name\": \"Do the dishes\"}, {\"id\": 2, \"name\": \"Buy catnips\"}]              |\n|             |        |             |             | null                                                                                |\n|             |        |             |             | {\"lat\": 48.8773406, \"lng\": \"2.327774\"}                                              |\n| debug       |        | object      |             | {\"elapsed\": 120000, \"isued_at\": 1529843640000000, \"resolved_at\": 1529843640120000\"} |\n| elapsed     | debug  | integer     | microsecond | 120000                                                                              |\n| issued_at   | debug  | integer     | microsecond | 1529843640000000                                                                    |\n| resolved_at | debug  | integr      | microsecond | 1529843640120000                                                                    |\n\n## List of libraries that implement JUR\n\n- PHP\n  - [khalyomede/php-jur](https://github.com/khalyomede/php-jur)\n- Javascript\n  - [khalyomede/jur-js](https://github.com/khalyomede/jur-js)\n\nMade an implementation? Please fill in an issue and I will add it to the list (or make a Pull Request for this).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhalyomede%2Fjur","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkhalyomede%2Fjur","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkhalyomede%2Fjur/lists"}