{"id":20119883,"url":"https://github.com/restocat/restocat","last_synced_at":"2025-05-06T14:32:51.167Z","repository":{"id":22180884,"uuid":"25512843","full_name":"restocat/restocat","owner":"restocat","description":"Restocat is a framework for create REST APIs","archived":false,"fork":false,"pushed_at":"2019-12-18T05:05:35.000Z","size":290,"stargazers_count":17,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-17T01:26:21.613Z","etag":null,"topics":["api-server","fast","framework","javascript","restful","restful-api","restocat"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/restocat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-10-21T09:06:42.000Z","updated_at":"2022-09-08T11:26:03.000Z","dependencies_parsed_at":"2022-07-18T21:49:14.000Z","dependency_job_id":null,"html_url":"https://github.com/restocat/restocat","commit_stats":null,"previous_names":["chetverikov/substance"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restocat%2Frestocat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restocat%2Frestocat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restocat%2Frestocat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/restocat%2Frestocat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/restocat","download_url":"https://codeload.github.com/restocat/restocat/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252703465,"owners_count":21790890,"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-server","fast","framework","javascript","restful","restful-api","restocat"],"created_at":"2024-11-13T19:17:36.898Z","updated_at":"2025-05-06T14:32:50.841Z","avatar_url":"https://github.com/restocat.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Restocat\n========\n\n[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/restocat/restocat?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![Travis CI](https://travis-ci.org/restocat/restocat.svg?branch=master)](https://travis-ci.org/restocat/restocat)\n[![codecov](https://codecov.io/gh/restocat/restocat/coverage.svg)](https://codecov.io/gh/restocat/restocat)\n[![Codeclimate](https://codeclimate.com/github/restocat/restocat/badges/gpa.svg)](https://codeclimate.com/github/restocat/restocat)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/restocat/restocat/master/docs/images/logo_full_wide.png\"\u003e\n\u003c/p\u003e\n\n# TL;DR\n\nInstall [Restocat CLI](https://www.npmjs.com/package/restocat-cli) using following command:\n\n```bash\nnpm install -g restocat-cli\n```\n\nUse Restocat CLI to create an empty project like this:\n\n```bash\nrestocat init\n```\n\n# Collections\n\nEach collection is declared as a directory with the collection.json file by default. When Restocat initializes the application it recursively searches for such directories starting with your project root, and then via first-level dependencies in the ./node_modules directory.\n\nThe collection.json file consists of following:\n\n* name – the name of the collection (optional). By default, this is the name of the directory. Please keep in mind that a parameter's name in a route definition must satisfy the regular expression /^[\\w-]+$/i\n* logic – a relative path to the file that exports a class or a constructor function for the collection's logic object (optional). By default, index.js will be used.\n* endpointDefault – a relative path to the file that exports a class or a constructor function for the collection's logic object (optional). By default - `true`\n* endpoints – map of the relationship between the route and handler (optional). By default:\n\n```javascript\n{\n    \"get /\": \"list\",\n    \"post /\": \"create\",\n    \"get /:id\": \"one\",\n    \"put /:id\": \"update\",\n    \"delete /:id\": \"delete\"\n}\n```\n\nAn example collection.json could look like this:\n\n```javascript\n{\n    \"name\": \"Entities\",\n    \"description\": \"Some awesome and cool collection\",\n    \"logic\": \"./Entities.js\",\n    \"endpoints\": {\n      \"delete /:id\": false, // don't use this endpoint\n      \"put /:id\": false,\n      \"post /\": false,\n      \"get /sub-entities/\": \"subEntities\", // custome endpoint\n      \"get /sub-entities/:id\": \"subEntity\"\n    }\n}\n```\n\nAlso, you can set your list of the endpoints by default:\n\n```javascript\n\nlocator.registerInstance('defaultEndpoints', {\n    'get /': 'list',\n    'post /': 'create',\n    'get /:id': 'one'\n})\n\n```\n\n# Context\n\nRestocat sets as the property `$context` for every instance of each `collection`.\n\n`$context` has at least the following properties and methods:\n\n* `this.$context.request` - the current request (IncomingMessage)\n* `this.$context.response` - the current response (ResponseServer)\n\n* `this.$context.location` – the current [URI](https://github.com/catberry/catberry-uri) object that constains the current location.\n* `this.$context.referrer` – the current [URI](https://github.com/catberry/catberry-uri) object that contains the current referrer.\n\n* `this.$context.locator` – the [Service Locator](https://github.com/catberry/catberry-locator) of the application.\n* `this.$context.forward(collection_name:String, handle_name:String)` – forwarding the current request to a other collection. This method is useful for creating a sub-collection flow \n* `this.$context.notFound()` - correct 404 response from the server\n* `this.$context.redirect(uri:String, statusCode:Number)` - correct http redirect\n* `this.$context.notSend()` - a message to restocat what to call the send method is not necessary, we'll do it. **Attention! It can not be caused by formatters and errorHandler**\n\n* `this.$context.name` - name of current collection\n* `this.$context.properties` - properties from `collection.json` for current collection\n* `this.$context.handleName` - handler name for current endpoint\n* `this.$context.state` - the keys found in the path\n\n# Events\n\nThere are two ways of listening to a Restocat event:\n\n* Subscribe on it using the Restocat application instance directly like this:\n\n```javascript\nconst Restocat = require('restocat');\nconst restocat = new Restocat();\n\nrestocat.events.on('error', error =\u003e {\n    // some action\n});\n```\n\n* Subscribe on it using the `this.$context` object of a collection using the same `on` or `once` methods.\n\n```javascript\nthis.$context.on('error', error =\u003e {\n    // some action\n});\n```\n\n## Event names and arguments\nHere is a list of Restocat events:\n\n| Event | When happens | Arguments |\n|---------------|-------------------------------|-----------------------------------------------------------|\n| trace | Trace message was sent | `String` |\n| debug | Debug message was sent | `String` |\n| info | Information message was sent | `String` |\n| warn | Warning message was sent | `String` |\n| error | Error message was sent | `String \\ Error` |\n| fatal | Fatal error message was sent | `String \\ Error` |\n| collectionLoaded | each collection is loaded | `{name: String, properties: Object, constructor: function}` |\n| allCollectionsLoaded | all collections are loaded | Loaded collections by their names |\n| incomingMessage | Request message | `IncomingMessage` |\n| responseServer | Response server | `ResponseServer`, `IncomingMessage` |\n\n# Content Negotiation\n\nIf you're not using $context.notSend() Restocat will automatically select the content-type to respond with, \nby finding the first registered formatter defined.\nAlso, note that if a content-type can't be negotiated, the default is `application/octet-stream`. Of course, you can always explicitly set the content-type:\n\n```javascript\n\n$context.response.setHeader('content-type', 'application/vnd.application+json');\n$context.response.send({hello: 'world'});\n\n```\n\nNote that there are typically at least three content-types supported by Restocat (json, text and binary). \nWhen you override or append to this, the \"priority\" might change; to ensure that the priority is set to what you want, \nyou should set a `q`-value on your formatter definitions, which will ensure sorting happens the way you want\n\nSo, if you are using the $context.notSend() and send the data manually, you should independently obtain and to use a formatter:\n\n```javascript\n\nconst formatterProvider = this._serviceLocator.resolve('formatterProvider');\nconst formatter = formatterProvider.getFormatter(this.$context);\n\nPromise.resolve(() =\u003e formatter(this.$context, myContent))\n    .then(content =\u003e this.$context.resposne.send(content));\n\n```\n\n\n## Formatters\n\nYou can add additional formatters to Restocat:\n\n```javascript\n\nconst Restocat = require('restocat');\nconst cat = new Restocat();\nconst server = cat.createServer();\n\nserver.register('formatter', {\n    \n  // context - current context with request and response; data - data for response\n  'text/plain; q=0.3': (context, data) =\u003e {\n    const string = String(data);\n\n    context.response.setHeader('Content-Length', Buffer.byteLength(string));\n    context.response.setHeader('X-FORMATTER', 'CUSTOM');\n\n    return string;\n  }\n});\n\n```\n\n# Not implemented handler (Not found)\n\nYou can add your handler of the situation when the specified url was never found handler:\n\n```javascript\n\nconst Restocat = require('restocat');\nconst cat = new Restocat();\nconst server = cat.createServer();\n\nserver.register('notImplementedHandler', $context =\u003e {\n  const NotFound = $context.locator.resolve('httpErrors').NotFoundError;\n  \n  return Promise.reject(new NotFound('Not found handler for current url'));\n});\n\n```\n\n# Request API (IncomingMessage)\nWraps all of the node [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage) APIs, events and properties, plus the following.\n\n* accepts(type:String|Array) - checks if the accept header is present and has the value requested\n* acceptsEncoding(type:String|Array) - checks if the request accepts the encoding types\n* getContentLength() - gets the content-length header off the request\n* getContentType() - gets the content-type header\n* getDate() - gets the _date property off the request when created the request\n* getLocation() - the current [URI](https://github.com/catberry/catberry-uri) object that constains the current location.\n* getRemoteAddr() - Remote address (the search sequence: x-forwarded-for -\u003e ip -\u003e remoteAddress -\u003e socket.remoteAddress -\u003e socket.socket.removeAddress)\n* getTime() - returns ms since epoch when request was setup\n* getTrailer(trailer:String) - returns any trailer header off the request. also, 'correct' any correctly spelled 'referrer' header to the actual spelling used.\n* getHeader(header:String) - returns any trailer header off the request. also, 'correct' any correctly spelled 'referrer' header to the actual spelling used.\n* getUserAgent() - the user-agent header\n* isChunked() - Check if the incoming request is chunked\n* isContentType(type:String) - Check if the incoming request contains the Content-Type header field, and if it contains the given mime type\n* isKeepAlive() - Check if the incoming request is kept aliv\n* isSecure() - Check if the incoming request is encrypted\n* isUpgradeRequest() - Check if the incoming request has been upgraded\n* isUpload() - Check if the incoming request is an upload verb\n\n# Response API (ServerResponse)\nWraps all of the node [http.ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse) APIs, events and properties, plus the following.\n\n* cache(type:String, options:Object) - sets the cache-control header. `type` defaults to _public_, and options currently only takes maxAge.\n* noCache() - turns off all cache related headers.\n* getTime() - returns ms since epoch when request was setup\n* charSet(type:String) - Appends the provided character set to the response's Content-Type\n* getCharSet() - get char set\n* getHeaders() - retrieves all headers off the response\n* setHeader(name:String, value:String) - sets headers on the response\n* json(code:Number, content:Object, headers:Object) - short hand method for: `res.contentType = 'json'; res.send({hello: 'world'});`\n* setLinkHeader(linkKey:String, rel:String) - sets the link header\n* send(code:Number, content:Object|Error|Buffer) - sends the response object. convenience method that handles: writeHead(), write(), end() \n* setStatus(code:Number) - sets the http status code on the response\n\nPlugins and useful links\n============\n* [Restocat Passport](https://github.com/restocat/restocat-passport)\n* [Restocat Logger](https://github.com/restocat/restocat-logger)\n* [Restocat Watcher](https://github.com/restocat/restocat-watcher)\n\n\n* [Example](https://github.com/restocat/restocat-example)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frestocat%2Frestocat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frestocat%2Frestocat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frestocat%2Frestocat/lists"}