{"id":20985791,"url":"https://github.com/vitorsalgado/drizzle-http","last_synced_at":"2025-05-14T17:32:18.398Z","repository":{"id":37027450,"uuid":"338920159","full_name":"vitorsalgado/drizzle-http","owner":"vitorsalgado","description":"Declarative HTTP clients using decorators. Available for Node.js, Browser and Deno.","archived":false,"fork":false,"pushed_at":"2024-10-18T18:08:35.000Z","size":16405,"stargazers_count":11,"open_issues_count":34,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-11-13T05:36:59.689Z","etag":null,"topics":["api","decorators","deno","http","http-client","monorepo","nodejs","npm","typescript","typescript-library"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/drizzle-http","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vitorsalgado.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2021-02-14T23:01:01.000Z","updated_at":"2024-10-21T12:43:40.000Z","dependencies_parsed_at":"2023-01-20T18:16:04.094Z","dependency_job_id":"21f25992-a76d-4391-b9d5-2f9c2ec62fd9","html_url":"https://github.com/vitorsalgado/drizzle-http","commit_stats":{"total_commits":371,"total_committers":3,"mean_commits":"123.66666666666667","dds":"0.16442048517520214","last_synced_commit":"32648db18acda8aeb9bcb92bd326a5b9f19d9f5a"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitorsalgado%2Fdrizzle-http","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitorsalgado%2Fdrizzle-http/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitorsalgado%2Fdrizzle-http/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitorsalgado%2Fdrizzle-http/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vitorsalgado","download_url":"https://codeload.github.com/vitorsalgado/drizzle-http/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225191623,"owners_count":17435578,"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","decorators","deno","http","http-client","monorepo","nodejs","npm","typescript","typescript-library"],"created_at":"2024-11-19T06:10:13.393Z","updated_at":"2024-11-19T06:10:15.394Z","avatar_url":"https://github.com/vitorsalgado.png","language":"TypeScript","readme":"\u003ch1 align='center'\u003eDrizzle HTTP\u003c/h1\u003e\n\n\u003cp align='center'\u003e\n  \u003cimg src=\"docs/assets/drizzle.png\" alt=\"Repository Logo\" width='100px' height='100px' /\u003e\n  \u003cbr /\u003e\n  \u003ci\u003eCreate API Clients with \u003cstrong\u003eDecorators\u003c/strong\u003e for Typescript and Javascript.\u003c/i\u003e\n\u003c/p\u003e\n\n\u003cp align='center'\u003e\n  \u003ca href='https://www.npmjs.com/settings/drizzle-http/packages' target='_blank'\u003e\u003cstrong\u003eNPM Packages\u003c/strong\u003e\u003c/a\u003e\u003cbr/\u003e\n  \u003ca href='https://deno.land/x/drizzle_http' target='_blank'\u003e\u003cstrong\u003eDeno\u003c/strong\u003e\u003c/a\u003e\u003cbr/\u003e\n\u003c/p\u003e\n\n\u003cp align='center'\u003e\n  \u003ca href=\"https://github.com/vitorsalgado/drizzle-http/actions/workflows/ci.yml\"\u003e\n    \u003cimg src=\"https://github.com/vitorsalgado/drizzle-http/actions/workflows/ci.yml/badge.svg\" alt=\"GitHub Action Status\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/vitorsalgado/drizzle-http/actions/workflows/deno.yml\"\u003e\n    \u003cimg src=\"https://github.com/vitorsalgado/drizzle-http/actions/workflows/deno.yml/badge.svg\" alt=\"Deno GitHub Action Status\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/vitorsalgado/drizzle-http\"\u003e\n    \u003cimg src=\"https://codecov.io/gh/vitorsalgado/drizzle-http/branch/main/graph/badge.svg?token=XU2YHXHAEH\" alt=\"Codecov\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/settings/drizzle-http/packages\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/@drizzle-http/core?logo=npm\" alt=\"NPM Package\" /\u003e  \n  \u003c/a\u003e\n  \u003ca href=\"https://deno.land/x/drizzle_http\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/available%20on-deno.land-lightgrey?logo=deno\u0026labelColor=black\" alt=\"Deno Package\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://conventionalcommits.org\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Conventional%20Commits-1.0.0-blue.svg?logo=git\" alt=\"Conventional Commits\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## What it is\n\n**Drizzle-HTTP** is library inspired by [Retrofit](https://github.com/square/retrofit)\nand [Feign](https://github.com/OpenFeign/feign), that let you create API clients using **decorators**.\n\n## Table of Contents\n\n- [Deno](deno)\n- [Installation](#installation)\n- [Packages](packages)\n- [Getting Started](#getting-started)\n- [Features](#features)\n- [Examples](examples)\n- [Common Issues](#common-issues)\n- [Benchmarks](#benchmarks)\n- [Contributing](#contributing)\n\n## Installation\n\nDrizzle-HTTP is a monorepo with several packages. You will need to install at least the core module, @drizzle-http/core,\nalong with one or more extensions.  \nFor a basic usage in a **Node.js Backend Environment**, you can install:\n\n```\nnpm i @drizzle-http/core\nnpm i @drizzle-http/undici\n```\n\nFor browser environments:\n\n```\nnpm i @drizzle-http/core\nnpm i @drizzle-http/fetch\n```\n\n## Getting Started\n\nBy default, request and response bodies will be handled as JSON. Will can change this with the appropriate decorators.  \nIt will not set the content type by default.\n\n### Overview\n\nUsage typically looks like the example below:\n\n```typescript\nimport { newAPI, Timeout } from '@drizzle-http/core'\nimport { GET } from \"@drizzle-http/core\";\nimport { Path } from \"@drizzle-http/core\";\nimport { Param } from \"@drizzle-http/core\";\nimport { POST } from \"@drizzle-http/core\";\nimport { Body } from \"@drizzle-http/core\";\nimport { HttpResponse } from \"@drizzle-http/core\";\nimport { PUT } from \"@drizzle-http/core\";\nimport { DELETE } from \"@drizzle-http/core\";\nimport { ParseErrorBody } from \"@drizzle-http/core\";\nimport { Query } from \"@drizzle-http/core\";\nimport { HeaderMap } from \"@drizzle-http/core\";\nimport { UndiciCallFactory } from \"@drizzle-http/undici\";\nimport { ContentType } from \"@drizzle-http/core\";\nimport { MediaTypes } from \"@drizzle-http/core\";\nimport { RawResponse } from \"@drizzle-http/core\";\n\n@Timeout(15e30)\n@Path('/customers')\n@HeaderMap({ 'x-app-id': 'example-app' })\n@ContentType(MediaTypes.APPLICATION_JSON)\nclass CustomerAPI {\n  @GET()\n  search (@Query('filter') filter: string, @Query('sort') sort: string): Promise\u003cCustomer[]\u003e {\n  }\n\n  @GET('/{id}')\n  @ParseErrorBody()\n  byId (@Param('id') id: string): Promise\u003cCustomer\u003e {\n  }\n\n  @POST()\n  @RawResponse()\n  add (@Body() customer: Customer): Promise\u003cHttpResponse\u003e {\n  }\n\n  @PUT('/{id}')\n  @RawResponse()\n  update (@Param('id') id: string, @Body() customer: Customer): Promise\u003cHttpResponse\u003e {\n  }\n\n  @DELETE('/{id}')\n  @RawResponse()\n  remove (@Param('id') id: string): Promise\u003cHttpResponse\u003e {\n  }\n}\n\nconst api = newAPI()\n  .baseUrl('https://example.com')\n  .callFactory(new UndiciCallFactory())\n  .createAPI(CustomerAPI)\n\nconst customer = await api.byId('100')\n```\n\n\u003c!---\n// @formatter:off\n--\u003e\n\n### Basic Decorators\n\n| Decorator             | Description                                                                                    | Target                     |\n|-----------------------|------------------------------------------------------------------------------------------------|----------------------------|\n| @GET()                | Define a HTTP `GET` request.                                                                   | Method                     |\n| @POST()               | Define a HTTP `POST` request.                                                                  | Method                     |\n| @PUT()                | Define a HTTP `PUT` request.                                                                   | Method                     |\n| @DELETE()             | Define a HTTP `DELETE` request.                                                                | Method                     | \n| @PATCH()              | Define a HTTP `PATCH` request.                                                                 | Method                     |\n| @OPTIONS()            | Define a HTTP `OPTIONS` request.                                                               | Method                     |\n| @HEAD()               | Define a HTTP `HEAD` request.                                                                  | Method                     |\n| @HTTP()               | Define a custom HTTP method for a request.                                                     | Method                     |\n| @Body()               | Mark the parameter that will be the request body.                                              | Parameter                  |\n| @Param()              | Define a path parameter that will replace a `{PARAM}` url template value                       | Parameter                  |\n| @Query()              | Define a querystring parameter                                                                 | Parameter                  |\n| @QueryName()          | Define a querystring name parameter                                                            | Parameter                  |\n| @Field()              | Define a `form-urlencoded` field parameter                                                     | Parameter                  |\n| @Header()             | Define a header parameter                                                                      | Parameter                  |\n| @HeaderMap()          | Define fixed headers                                                                           | Class, Method              |\n| @FormUrlEncoded()     | Define a `form-urlencoded` request                                                             | Class, Method              |\n| @Multipart()          | Create a multipart/form-data request (**Fetch Only**)                                          | Class, Method              |\n| @Part()               | Mark a parameter as a part of multipart/form-data request body (**Fetch Only**)                | Parameter                  |\n| @BodyKey()            | Change the name of part in a multipart/form-data request (**Fetch Only**)                      | Parameter                  |\n| @Accept()             | Define `Accept` header.                                                                        | Class, Method              |\n| @ContentType()        | Define `Content-Type` header.                                                                  | Class, Method              | \n| @Path()               | Define an additional url path. The value accepts template parameters.                          | Class                      |\n| @Abort()              | Configure request cancellation. Pass a `Event Emitter` instance. Cancel with an `abort` event. | Class, Method or Parameter |\n| @Timeout()            | Define the timeouts of a request                                                               | Class, Method              |\n| @ParseErrorBody()     | Parse error body. Can use a custom body converter                                              | Class, Method              |\n| @NoDrizzleUserAgent() | Remove Drizzle-HTTP custom user-agent header                                                   | Class                      |\n| @JsonRequest()        | Use JSON request body converter (**default**)                                                  | Class, Method              |\n| @JsonResponse()       | Use JSON response converter (**default**)                                                      | Class, Method              |\n| @UseJsonConv()        | Use JSON request/response converters (**default**)                                             | Class, Method              |\n| @PlainTextRequest()   | Use plain text request body converter                                                          | Class, Method              |\n| @PlainTextResponse()  | Use plain text response converter                                                              | Class, Method              |\n| @UsePlainTextConv()   | Use plain text request/response converters                                                     | Class, Method              |\n| @RequestType()        | Define a custom request body converter                                                         | Class, Method              |\n| @ResponseType()       | Define a custom response converter                                                             | Class, Method              |\n| @Model()              | Define a parameter that will hold the request definition. Used along with @To() decorator      | Class, Method              |\n| @To()                 | Map @Model() class properties and methods to a request                                         | Class, Method              |\n\n\u003c!---\n// @formatter:on\n--\u003e\n\n### Defaults\n\nDefault values that Drizzle starts with. All values can be overridden using decorators.\n\n- Timeout: **30 seconds**\n- Request Body Converter: **JSON**\n- Response Body Converter: **JSON**\n\n### Error Handling\n\nWhen methods are not decorated with `@RawResponse()`, Drizzle throws an `HttpError` with the following structure:\n\n```\n{\n  message: 'Request failed with status code: 400',\n  code: 'DZ_ERR_HTTP',\n  request: {\n    url: 'https://example.com/test,\n    method: 'GET',\n    headers: Headers,\n    body: ''\n  },\n  response: {\n    headers: Headers,\n    status: 400,\n    statusText: ''\n    body: 'error from server'\n  }\n}\n```\n\nWhen you want to parse the error response body to, for example a JSON object, use `@ParseErrorBody()`. By default,\n@ParseErrorBody() use the same response converter used by the success scenario. If you need a different converter for\nthe error body, pass the name of the converter to the decorator. E.g.: `@ParseErrorBody(BuiltInConv.TEXT)`.\n\n## Features\n\n- Define HTTP requests with decorators, including path parameters, querystring, headers, body and so on.\n- Extensible\n- Custom response adapters\n- Request interceptors\n- Abort requests\n- Timeouts\n- Parse responses to objects or get the raw response in a fetch like format\n- Parse error response bodies\n- **RxJs** support with [RxJs Adapter](packages/drizzle-rxjs)\n- Map responses with [Response Mapper Adapter](packages/drizzle-response-mapper)\n- **Circuit Breaker** with **Opossum** with [this adapter](packages/drizzle-opossum-circuit-breaker)\n- **Deno** support\n\n### Browser\n\nFor Browser usage, take a look on [this implementation](packages/drizzle-fetch). It uses **fetch** to make HTTP\nrequests.\n\n### Deno\n\nA version for **Deno** is available on [https://deno.land/x/drizzle_http](https://deno.land/x/drizzle_http).  \nThe Deno version is simpler than the one available for Node.js. It contains the **core** module and a fetch client\nimplementation specific for Deno.  \nMore details and usage example [here](deno).\n\n### Interceptors\n\nYou can intercept requests and responses using [Interceptors](packages/drizzle-core/Interceptor.ts).  \nYou can a simple function, `chain =\u003e {}`, an `Intepcetor` interface implementation or an `InterceptorFactory`\nimplementation, if you need more configurations.  \nTake a look on the examples below:\n\n```typescript\nclass CustomerAPI {\n  @GET('/{id}')\n  getById (@Param('id') id: string): Promise\u003cCustomer\u003e {\n    return noop(id)\n  }\n}\n\nconst api = newAPI()\n  .addInterceptor(async chain =\u003e {\n    console.log('before request')\n\n    const response = await chain.proceed(chain.request())\n\n    console.log('after request')\n\n    return response\n  })\n  .baseUrl('https://example.com')\n  .callFactory(new UndiciCallFactory())\n  .createAPI(CustomerAPI)\n```\n\n### Circuit Breaker\n\nWith the package [@drizzle-http/opossum-circuit-breaker](packages/drizzle-opossum-circuit-breaker), you can protect your\nendpoints with circuit breakers.  \nIt uses [Opossum](https://github.com/nodeshift/opossum) circuit breaker implementation.  \nSee a basic demonstration below. More details [here](packages/drizzle-opossum-circuit-breaker).\n\n```typescript\nimport { CircuitBreaker } from \"@drizzle-http/opossum-circuit-breaker\";\nimport { Fallback } from \"@drizzle-http/opossum-circuit-breaker\";\n\n@Timeout(15e30)\n@Path('/customers')\n@HeaderMap({ 'x-app-id': 'example-app' })\nclass CustomerAPI {\n  @GET()\n  @CircuitBreaker()\n  search (@Query('filter') filter: string, @Query('sort') sort: string): Promise\u003cCustomer[]\u003e {\n  }\n\n  @GET('/{id}')\n  @CircuitBreaker()\n  @Fallback((id: string, error: Error) =\u003e { /** fallback logic **/\n  })\n  byId (@Param('id') id: string): Promise\u003cCustomer\u003e {\n  }\n}\n```\n\n### Raw HTTP Response\n\nBy the default, HTTP success responses you be parsed and resolved and http errors will be rejected. If you want the raw\nHTTP response, including headers, status codes, body stream, decorate your method with `@RawResponse()` and the return\nwill be a `Promise\u003cHttpResponse\u003e`, similar to Fetch. In this case, HTTP errors will not be rejected.\n\n### Form URL Encoded\n\nTo make `application/x-www-form-urlencoded` request, decorate your class or method with `@FormUrlEncoded()`.  \nUse `@Field()` to define a parameter as a form field entry.  \nIf using `@Body()`, object keys will be converted to url form encoded format.\n\n## Common Issues\n\n### ESLint and TS Check Problems\n\nThe API class methods doesn't need to have a body, but TS and some ESLint configurations will complain with the empty\nbody and maybe the unused parameters. To solve this, you can:\n\n- Use the helper function `noop()` in all method bodies. This function does nothing, but it will have the same return\n  type as your method, and you can pass all method arguments removing any lint issues.\n- Disable TS check for the API class file with the comment: `// @ts-nocheck`\n- Disable or relax ESLint checks for the file or class\n\nExample:\n\n```typescript\nimport { noop } from \"@drizzle-http/core\";\n\nclass CustomerAPI {\n  @GET('/{id}')\n  byId (@Param('id') id: string): Promise\u003cCustomer\u003e {\n    return noop(id)\n  }\n\n  @POST()\n  @ParseErrorBody()\n  @RawResponse()\n  add (@Body() customer: Customer): Promise\u003cHttpResponse\u003e {\n    return noop(customer)\n  }\n}\n```\n\n### Request/Response Mismatch\n\nYou need to be very explicitly regarding the API class and method configurations as Drizzle is unable to detect stuff\nlike the generic return type of methods. For example, if you want the raw response, to gain access to all the details of\na http response, you need to explicitly decorate your method with `@RawResponse()`.\n\n## Benchmarks\n\n### Run\n\n```\nnpm run benchmark\n```\n\n### Results\n\n```\nMachine: MacBook Pro (13-inch, 2019)\nProcessor: 2,8 GHz Quad-Core Intel Core i7\nMemory: 16 GB 2133 MHz LPDDR3\nNode: 15\n```\n\n| Tests                                       | Samples | Result         | Tolerance | Difference with slowest |\n|---------------------------------------------|---------|----------------|-----------|-------------------------|\n| got                                         | 10      | 360.02 req/sec | ± 1.99 %  | -                       |\n| axios                                       | 10      | 622.72 req/sec | ± 2.14 %  | + 72.97 %               |\n| http                                        | 10      | 749.67 req/sec | ± 2.19 %  | + 108.23 %              |\n| drizzle-http - (undici) - (circuit breaker) | 10      | 762.81 req/sec | ± 2.95 %  | + 111.88 %              |\n| drizzle-http - (undici)                     | 10      | 781.68 req/sec | ± 2.22 %  | + 117.12 %              |\n| undici                                      | 10      | 799.53 req/sec | ± 2.05 %  | + 122.08 %              |\n\nThis benchmark consists in a client with multiple connections performing calls to a server that responds a 80kb JSON.\n\n## Contributing\n\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-blue.svg)](https://conventionalcommits.org)\n[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat)](https://github.com/prettier/prettier)\n[![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org/)\n[![jest](https://jestjs.io/img/jest-badge.svg)](https://github.com/facebook/jest)\n\nSee [CONTRIBUTING](CONTRIBUTING.md) for more details.\n\n## License\n\nDrizzle HTTP is [MIT Licensed](LICENSE).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitorsalgado%2Fdrizzle-http","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvitorsalgado%2Fdrizzle-http","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitorsalgado%2Fdrizzle-http/lists"}