{"id":18331183,"url":"https://github.com/bankole2000/typed-response-formatter","last_synced_at":"2026-01-20T16:03:20.371Z","repository":{"id":242544401,"uuid":"809843738","full_name":"Bankole2000/typed-response-formatter","owner":"Bankole2000","description":"Service Response formatter but typed","archived":false,"fork":false,"pushed_at":"2024-06-12T02:16:01.000Z","size":331,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-29T17:53:50.594Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/Bankole2000.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":["bankole2000"]}},"created_at":"2024-06-03T14:54:55.000Z","updated_at":"2024-06-12T02:16:05.000Z","dependencies_parsed_at":"2024-11-05T19:54:48.878Z","dependency_job_id":null,"html_url":"https://github.com/Bankole2000/typed-response-formatter","commit_stats":null,"previous_names":["bankole2000/typed-response-formatter"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/Bankole2000/typed-response-formatter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bankole2000%2Ftyped-response-formatter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bankole2000%2Ftyped-response-formatter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bankole2000%2Ftyped-response-formatter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bankole2000%2Ftyped-response-formatter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Bankole2000","download_url":"https://codeload.github.com/Bankole2000/typed-response-formatter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Bankole2000%2Ftyped-response-formatter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28606311,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T14:45:23.139Z","status":"ssl_error","status_checked_at":"2026-01-20T14:44:16.929Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-05T19:28:36.073Z","updated_at":"2026-01-20T16:03:20.349Z","avatar_url":"https://github.com/Bankole2000.png","language":"TypeScript","funding_links":["https://github.com/sponsors/bankole2000"],"categories":[],"sub_categories":[],"readme":"# Service Response Formatter\n\nA Collection of utility functions to quickly scaffold and standardize server-side function returns and http responses with nodejs.\n\n\u003c!-- PROJECT SHIELDS --\u003e\n\u003c!--\n*** I'm using markdown \"reference style\" links for readability.\n*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).\n*** See the bottom of this document for the declaration of the reference variables\n*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.\n*** https://www.markdownguide.org/basic-syntax/#reference-style-links\n--\u003e\n\u003c!-- [![Contributors][contributors-shield]][contributors-url]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![MIT License][license-shield]][license-url]\n[![LinkedIn][linkedin-shield]][linkedin-url] --\u003e\n\n\u003c!-- PROJECT LOGO --\u003e\n\u003cbr /\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/Bankole2000/service-reponse-formatter\"\u003e\n    \u003cimg src=\"./http.png\" alt=\"HTTP Icon\" width=\"100\" height=\"100\"\u003e\n  \u003c/a\u003e\n\n  \u003ch3 align=\"center\"\u003e Typed Service Response\u003c/h3\u003e\n\n  \u003cp align=\"center\"\u003e\n    Yet Another HTTP Response Formatter\n  \u003c/p\u003e\n\u003c/p\u003e\n\n\u003c!-- TABLE OF CONTENTS --\u003e\n\u003cdetails open=\"open\"\u003e\n  \u003csummary\u003eTable of Contents\u003c/summary\u003e\n  \u003cul\u003e\n    \u003cli\u003e\n      \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\n      \u003ca href=\"#Usage\"\u003eUsage\u003c/a\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"#generating-serviceresponse\"\u003eGenerating \u003ckbd\u003eServiceResponse\u003c/kbd\u003e\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#generating-tstatus\"\u003eGenerating \u003ckbd\u003eTStatus\u003c/kbd\u003e\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\n      \u003ca href=\"#list-of-status-codes-types--generator-fxns\"\u003eStatus Codes List\u003c/a\u003e\n      \u003ca href=\"#other-utilities\"\u003eOther Utilities\u003c/a\u003e\n    \u003c/li\u003e\n    \u003c!-- \u003cli\u003e\n      \u003ca href=\"#getting-started\"\u003eGetting Started\u003c/a\u003e\n      \u003cul\u003e\n        \u003cli\u003e\u003ca href=\"#prerequisites\"\u003ePrerequisites\u003c/a\u003e\u003c/li\u003e\n        \u003cli\u003e\u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e\u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#roadmap\"\u003eRoadmap\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#contributing\"\u003eContributing\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#contact\"\u003eContact\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003e\u003ca href=\"#acknowledgements\"\u003eAcknowledgements\u003c/a\u003e\u003c/li\u003e --\u003e\n  \u003c/ul\u003e\n\u003c/details\u003e\n\n## Installation\n\nMeant to be installed on a __nodejs express__ server with either JavaScript or Typescript\n\n```sh\nnpm install @neoncoder/service-response\n```\n\nThe package exposes types, interfaces, and functions to help standardize and easily scaffold json http responses and function returns\n\n`ServiceResponse` - generic class for http responses\n\n```ts\n// meta data object to add to data object\n// useful for content not explicitly in resource type \n// like pagination etc\ninterface dataMeta {\n  meta?: any\n}\n\ntype ServiceResponse\u003cKeys extends string, T = any\u003e = {\n  // key is data property where value is of Type T\n  // data sent to the client (including pagination etc)\n  data?: ({ [K in Keys]: T | T[] } \u0026 dataMeta) | null;, \n  // error summary (e.g. error.message)\n  errMessage?: string | undefined | null, \n  // error details (raw error object / instance)\n  error?: unknown, \n  // suggested fixes to help client / end user (e.g. contact support)\n  fix?: string | undefined | null, \n  // short message describing operation result (e.g. 'Login successful')\n  message: string, \n  // any req, res metadata (e.g. requestId, appId, version, timestamps etc)\n  meta?: any, \n  // if using accesstoken and token is refreshed, you can send the new token here\n  newAccessToken?: string | undefined | null, \n  // was the operation successful or not?\n  success: boolean, \n  // server response status code - i.e. 200, 201, 404, etc\n  statusCode: number, \n}\n```\n\n`TStatus` - Interface for relaying Data Access / CRUD operation results (or function output in general)\n\n```ts\ntype TStatus = {\n  // operation status code - i.e. 200, 201, 404, etc\n  code: number,\n  // short message describing result (e.g. 'Created successfully')\n  message: string,\n  // suggested fixes (optional e.g. contact support)\n  fix?: string,\n  // resultant data from operation (including pagination etc)\n  data?: any,\n  // error details (raw error object / instance)\n  error?: any,\n  // \"OK\" | \"Created\" | \"BadRequest\" | \"Unauthorized\" etc\n  statusType: string\n}\n```\n\n## Usage\n\n### Generating `ServiceResponse`\n\n- Import and invoke the generator function (e.g. `OK`, `NotFound`, `Created`, `Unauthorized` etc) passing in none or any of the `ServiceResponse` type options (i.e. `{message, success, data, fix, meta, error, errMessage, newAccessToken}`);\n\n```ts\nimport express, { Request, Response } from 'express';\nimport { OK, NotFound, Unauthorized, ServiceResponse } from '@neoncoder/service-response'\n\nconst app = express();\n\n// generate OK - 200 response\napp.get('/endpoint', async (req: Request, res: Response) =\u003e {\n  const someData = await getSomeData()\n  const sr: ServiceReponse = OK({data: someData})\n  res.status(sr.statusCode).send(sr)\n})\n\n// generate NotFound - 404 response\napp.get('/not-found', async (req: Request, res: Response) =\u003e {\n  const sr: ServiceReponse = NotFound({message: 'This route does not exist'})\n  res.status(sr.statusCode).send(sr)\n})\n\n// generate Unauthorized - 401 response\napp.get('/unauthorized', async (req: Request, res: Response) =\u003e {\n  if(!checkUserAuth()){\n    const sr: ServiceReponse = Unauthorized({})\n    return res.status(sr.statusCode).send(sr)\n  }\n  next()\n})\n```\n\n- Or Use the `Rez` object and call the functions from there\n  \n```ts\nimport express, { Request, Response } from 'express';\nimport { Rez, ServiceResponse } from '@neoncoder/service-response'\n\nconst app = express();\n\n// generate OK - 200 response\napp.get('/endpoint', async (req: Request, res: Response) =\u003e {\n  const someData = await getSomeData()\n  const sr: ServiceReponse = Rez.OK({data: someData})\n  res.status(sr.statusCode).send(sr)\n})\n\n// generate NotFound - 404 response\napp.get('/not-found', async (req: Request, res: Response) =\u003e {\n  const sr: ServiceReponse = Rez.NotFound({fix: 'check the route exists'})\n  res.status(sr.statusCode).send(sr)\n})\n```\n\n### Generating `TStatus`\n\n- The `statusMap.get(\u003cstatusCode\u003e)` method returns a function that takes in an options object as parameter (i.e.`{message, fix, error, data}`) to return a `TStatus` object;\n\n```ts\nimport express, { Request, Response } from 'express';\nimport { PrismaClient } from '@prisma/client'\nimport { statusMap, TStatus, Rez } from '@neoncoder/service-response'\n\nconst prisma = new PrismaClient()\nconst app = express();\n\napp.get('/user/:id', async (req: Request, res: Response) =\u003e {\n  let result: TStatus;\n  try {\n    const user = await prisma.user.findUnique({\n      where: {\n        id: req.params.id\n      }\n    })\n    // if user is found return 200 with user data, else return 404\n    result = user \n      ? statusMap.get(200)!({data: user}) \n      : statusMap.get(404)!({})\n  } catch (error: any) {\n    result = statusMap.get(500)!({error})\n  }\n  // Quickly generate a ServiceResponse from the TStatus object / result\n  const sr: ServiceResponse = Rez[result.statusType]({...result})\n  return res.status(sr.statusCode).send(sr)\n})\n```\n\n- The `statuses` object contains a predefined list of TStatus Objects that can be referenced by the Status Type. Using the previous example with the `statuses` object becomes:\n\n```ts\nimport express, { Request, Response } from 'express';\nimport { PrismaClient } from '@prisma/client'\nimport { statuses, TStatus, Rez } from '@neoncoder/service-response'\n\nconst prisma = new PrismaClient()\nconst app = express();\n\napp.get('/user/:id', async (req: Request, res: Response) =\u003e {\n  let result: TStatus;\n  try {\n    const user = await prisma.user.findUnique({\n      where: {\n        id: req.params.id\n      }\n    })\n    // if user is found return 200 with user data, else return 404\n    result = user \n      ? {...statuses.OK, data: user}\n      : {...statuses.NotFound}\n  } catch (error: any) {\n    result = {...statuses.InternalServerError, error}\n  }\n  // Quickly generate a ServiceResponse from the TStatus object / result\n  const sr: ServiceResponse = Rez[result.statusType]({...result})\n  return res.status(sr.statusCode).send(sr)\n})\n```\n\n## List of Status Codes, Types \u0026 Generator Fxns\n\n| Status Code | StatusType             | Generate `ServiceResponse`                                     | Generate `TStatus`        |\n| ----------- | ---------------------- | -------------------------------------------------------------- | ------------------------- |\n| 200         | `OK`                   | `OK({})` \\|\\| `Rez.OK({})`                                     | `statusMap.get(200)!({})` |\n| 201         | `Created`              | `Created({})` \\|\\| `Rez.Created({})`                           | `statusMap.get(201)!({})` |\n| 204         | `NoContent`            | `NoContent({})` \\|\\| `Rez.NoContent({})`                       | `statusMap.get(204)!({})` |\n| 400         | `BadRequest`           | `BadRequest({})` \\|\\| `Rez.BadRequest({})`                     | `statusMap.get(400)!({})` |\n| 401         | `Unauthorized`         | `Unauthorized({})` \\|\\| `Rez.Unauthorized({})`                 | `statusMap.get(401)!({})` |\n| 403         | `Forbidden`            | `Forbidden({})` \\|\\| `Rez.Forbidden({})`                       | `statusMap.get(403)!({})` |\n| 404         | `NotFound`             | `NotFound({})` \\|\\| `Rez.NotFound({})`                         | `statusMap.get(404)!({})` |\n| 405         | `MethodNotAllowed`     | `MethodNotAllowed({})` \\|\\| `Rez.MethodNotAllowed({})`         | `statusMap.get(405)!({})` |\n| 408         | `TimeoutError`         | `TimeoutError({})` \\|\\| `Rez.TimeoutError({})`                 | `statusMap.get(408)!({})` |\n| 415         | `UnsupportedMediaType` | `UnsupportedMediaType({})` \\|\\| `Rez.UnsupportedMediaType({})` | `statusMap.get(415)!({})` |\n| 417         | `ExpectationFailed`    | `ExpectationFailed({})` \\|\\| `Rez.ExpectationFailed({})`       | `statusMap.get(417)!({})` |\n| 422         | `UnprocessableEntity`  | `UnprocessableEntity({})` \\|\\| `Rez.UnprocessableEntity({})`   | `statusMap.get(422)!({})` |\n| 429         | `TooManyRequests`      | `TooManyRequests({})` \\|\\| `Rez.TooManyRequests({})`           | `statusMap.get(429)!({})` |\n| 500         | `InternalServerError`  | `InternalServerError({})` \\|\\| `Rez.InternalServerError({})`   | `statusMap.get(500)!({})` |\n| 503         | `ServiceUnavailable`   | `ServiceUnavailable({})` \\|\\| `Rez.ServiceUnavailable({})`     | `statusMap.get(503)!({})` |\n| 504         | `GatewayTimeout`       | `GatewayTimeout({})` \\|\\| `Rez.GatewayTimeout({})`             | `statusMap.get(504)!({})` |\n\n## Other Utilities\n\nThe package also provides an `IDataAccess` interface for easy implementation of the `TStatus` type\n\n```ts\ninterface IDataAccess {\n  result: TStatus | undefined\n}\n```\n\nAn example usecase could be ensuring that a DBAL class returns data in a consistent shape. In the example below a class is used to manage a resource called `Resource` (could be a User, or Item, or Post etc)\n\n```ts\n// Data Object Class file\nimport {IDataAccess, TStatus, statusMap} from '@neoncoder/service-response'\nimport { PrismaClient, User, UserCreateInput PrismaClientValidationError, PrismaClientKnownRequestError } from '@prisma/client'\n\nclass UserDBALClass implements IDataAccess {\n\n  user: User | undefined\n  result: TSatus | undefined\n  prisma: PrismaClient\n\n  constructor(){\n    this.prisma = new PrismaClient()\n  }\n\n  async create(data: UserCreateInput){\n    try{\n      const newUser = await prisma.user.create({data})\n      this.result = statusMap.get(201)!({data: newUser})\n      this.user = newUser\n    } catch (err){\n      this.formatError(err)\n    } finally {\n      return this\n    }\n  }\n\n  async update(id: string, data: UserUpdateInput){\n    try{\n      const updatedUser = await prisma.user.update({where: {id}, data})\n      this.result = statusMap.get(201)!({data: updatedUser})\n    } catch (err){\n      this.formatError(err)\n    } finally {\n      return this\n    }\n  }\n\n  formatError(error: unknown, msg = 'An error occurred') {\n    const message = error.message ?? msg\n    if(\n        err instanceof PrismaClientValidationError \n        ||err instanceof PrismaClientKnownRequestError\n      ) {\n        return this.result = statusMap.get(400)!({error, message});\n      }\n      this.result = statusMap.get(500)!({error, message})\n  }\n}\n\n// In Some other file\nimport {Request, Response} from 'express';\napp.post('/users', async (req: Request, res: Response) =\u003e {\n  const {name, gender} = req.body\n  const userA = new UserDBALClass()\n  const {result} = await userA.create({name}).update(userA.user?.id, {gender})\n  const sr: ServiceResponse = result \n    ? Rez[result.statusType]({...result})\n    : Rez.InternalServerError({}) \n  return res.status(sr.statusCode).send(sr)\n});\n```\n\n\u003c!-- * [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet)\n* [Img Shields](https://shields.io)\n* [Choose an Open Source License](https://choosealicense.com)\n* [GitHub Pages](https://pages.github.com)\n* [Animate.css](https://daneden.github.io/animate.css)\n* [Loaders.css](https://connoratherton.com/loaders)\n* [Slick Carousel](https://kenwheeler.github.io/slick)\n* [Smooth Scroll](https://github.com/cferdinandi/smooth-scroll)\n* [Sticky Kit](http://leafo.net/sticky-kit)\n* [JVectorMap](http://jvectormap.com)\n* [Font Awesome](https://fontawesome.com) --\u003e\n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\u003c!-- https://www.markdownguide.org/basic-syntax/#reference-style-links --\u003e\n\u003c!-- [product-screenshot]: https://raw.githubusercontent.com/othneildrew/Best-README-Template/master/images/screenshot.png --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbankole2000%2Ftyped-response-formatter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbankole2000%2Ftyped-response-formatter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbankole2000%2Ftyped-response-formatter/lists"}