{"id":13727033,"url":"https://github.com/Coobaha/typed-fastify","last_synced_at":"2025-05-07T22:30:46.453Z","repository":{"id":39907811,"uuid":"339754680","full_name":"Coobaha/typed-fastify","owner":"Coobaha","description":"Better typescript support and runtime safety of fastify handlers","archived":false,"fork":false,"pushed_at":"2025-01-13T08:00:20.000Z","size":1975,"stargazers_count":67,"open_issues_count":11,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-05T06:08:06.617Z","etag":null,"topics":["fastify","json-schema","schema","schema-generation","types","typescript","validation"],"latest_commit_sha":null,"homepage":"https://tfs.cooba.me","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/Coobaha.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-02-17T14:45:25.000Z","updated_at":"2025-01-24T17:36:59.000Z","dependencies_parsed_at":"2023-11-19T09:26:47.913Z","dependency_job_id":"6785b043-d4e8-45c8-87e6-ebaf9fb18ab2","html_url":"https://github.com/Coobaha/typed-fastify","commit_stats":{"total_commits":104,"total_committers":5,"mean_commits":20.8,"dds":"0.47115384615384615","last_synced_commit":"3e08aadd963f6365fca818ddb76e2f379e35136d"},"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Coobaha%2Ftyped-fastify","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Coobaha%2Ftyped-fastify/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Coobaha%2Ftyped-fastify/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Coobaha%2Ftyped-fastify/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Coobaha","download_url":"https://codeload.github.com/Coobaha/typed-fastify/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252463292,"owners_count":21751760,"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":["fastify","json-schema","schema","schema-generation","types","typescript","validation"],"created_at":"2024-08-03T01:03:36.573Z","updated_at":"2025-05-07T22:30:45.319Z","avatar_url":"https://github.com/Coobaha.png","language":"TypeScript","funding_links":[],"categories":["typescript","TypeScript"],"sub_categories":[],"readme":"# Typed Fastify\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/coobaha/typed-fastify/main.yml?branch=main\u0026logo=github\u0026style=for-the-badge)](https://github.com/coobaha/typed-fastify/actions/workflows/main.yml)\n[![Coverage Status](https://img.shields.io/coveralls/github/Coobaha/typed-fastify.svg?style=for-the-badge)](https://coveralls.io/github/Coobaha/typed-fastify?branch=main)\n[![NPM Version](https://img.shields.io/npm/v/@coobaha/typed-fastify.svg?style=for-the-badge)](https://www.npmjs.com/package/@coobaha/typed-fastify)\n[![](https://img.shields.io/npm/dm/@coobaha/typed-fastify.svg?style=for-the-badge)](https://www.npmjs.com/package/@coobaha/typed-fastify)\n\n**[Online docs](https://tfs.cooba.me)**\n\nThis package adds strong TypeScript support to Fastify request handlers and enforces\nhandlers to have typed schema which is used to validate request params and replies. From this schema it does two\nthings:\n\n- static typechecking against TypeScript Schema\n  - `request.body`\n  - `request.headers`\n  - `request.querystring`\n  - `request.params`\n  - `route.path.params` are also inferred and mapped to `request.params`, it is also not possible to make a typo in schema params\n  - `reply` is always based on status, developer won't be able to use plain `reply.send()` but\n    forced to explicitly set status first, based on which response type will be inferred\n- JSON schema generation from TS Schema (using [typescript-json-schema](https://github.com/YousefED/typescript-json-schema) with custom\n  transforms, all `@tjs` annotations can be used to fine-tune output)\n  - since we use `typejescript-json-schema`: all known limitations of lib are inherited:\n    - Records are not transformed correctly, use `{ [k: string]: string }` instead or hint with `@tjs`\n- Runtime validation using generated JSON schema (optional but strongly recommended as it brings extra safety to runtime and ensures that code assumptions about data are correct)\n\n[demo video](https://user-images.githubusercontent.com/2446638/108409543-08b45f00-722f-11eb-905c-06505b57f5fe.mp4)\n\n## Usage\n\n```sh\nnpm i @coobaha/typed-fastify\n\npnpm i @coobaha/typed-fastify\n\nyarn add @coobaha/typed-fastify\n\n```\n\nExample of service we want to build\n\n```\nGET / =\u003e Hello ($querystring.name || world)\n```\n\nSimple implementation without schema generation will be following\n\n```typescript\nimport addSchema, { Schema } from '@coobaha/typed-fastify';\nimport fastify from 'fastify';\n\nexport interface ExampleSchema extends Schema {\n  paths: {\n    'GET /': {\n      request: {\n        querystring: {\n          name?: string;\n        };\n      };\n      response: {\n        200: {\n          content: string;\n        };\n      };\n    };\n  };\n}\n\nconst exampleService: Service\u003cExampleSchema\u003e = {\n  'GET /': (req, reply) =\u003e {\n    // typescript will infer correct types for us\n    const name = req.query.name ?? 'World';\n\n    // Calling send directly is not allowed\n    // reply.send(`Hello ${name}`)\n    // Calling send with wrong payload will result in an error\n    // reply.status(200).send(new Date())\n\n    return reply.status(200).send(`Hello ${name}`);\n  },\n};\n\nconst app = fastify();\naddSchema(app, {\n  // it is strongly recommended to generate json schema to guaruntee runtime validity\n  jsonSchema: {},\n  service: exampleService,\n});\n\n// Start listening.\napp.listen(3000, (err: any) =\u003e {\n  if (err) {\n    app.log.error(err);\n    process.exit(1);\n  }\n});\n```\n\nComplex examples can be found [typescript tests](./test/typed-fastify.test-d.ts) and\nin [integration.test.ts](./test/integration.test.ts).\n\n## JSON schema generation\n\nYou can generate json schema from your TS types by using `typed-fastify-schema` or `tfs` bins\n\n```sh\nnpx tfs gen\n```\n\n```\ntfs gen [files]\n\nGenerates json schemas next to corresponding ts files\n\nPositionals:\n  files  glob pattern of files                               [string] [required]\n\nOptions:\n  --help     Show help                                                 [boolean]\n  --version  Show version number                                       [boolean]\n```\n\n```sh\n# it will generate example_schema.gen.json next to file\nnpx tfs gen example_schema.ts\n```\n\nWhen schema is generated - just pass it to plugin to have runtime validations 🎉\n\n```typescript\nimport jsonSchema from './example_schema.gen.json';\n\n// ...\n\naddSchema(app, {\n  jsonSchema,\n  service,\n});\n```\n\n### Writing service\n\n1. Handlers in one object\n   Type inference will work nicely in this case, you just make TS happy and things are working 🥳\n\n2. Handlers in a different file or separate functions - you will need to hint TS with exact type of handler.\n\n```typescript\nimport { RequestHandler, Schema } from '@coobaha/typed-fastify';\n\ninterface MySchema extends Schema {}\n\nconst myHandler: RequestHandler\u003cMySchema, 'GET /hello'\u003e['AsRoute'] = (req, reply) =\u003e {};\n```\n\n3. When you want to have complex shared handler for multiple endpoints that intersect (share same\n   props)\n\n```typescript\nimport { RequestHandler, Schema } from '@coobaha/typed-fastify';\n\ninterface MySchema extends Schema {}\n\nconst myHandlers: RequestHandler\u003cMySchema, 'GET /hello' | `GET /hello2`\u003e['AsRoute'] = (req, reply) =\u003e {};\n```\n\n4. Sometimes properties won't be the same (for instance GET never has body and POST will). In this case you will probably be asked to add types to function params\n\n```typescript\nimport { RequestHandler, Schema } from '@coobaha/typed-fastify';\n\ninterface MySchema extends Schema {}\n\ntype MyHandlers = RequestHandler\u003cMySchema, 'GET /hello' | `POST /hello`\u003e;\nconst myHandlers = (req: MyHandlers['Request'], reply: MyHandlers['Reply']): MyHandlers['Return'] =\u003e {};\n\n// if handler is async/await\nconst myHandlersAsync = async (req: MyHandlers['Request'], reply: MyHandlers['Reply']): MyHandlers['ReturnAsync'] =\u003e {};\n\naddSchema(app, {\n  jsonSchema: {},\n  service: {\n    'GET /hello': myHandlers,\n    'GET /hello2': myHandlers,\n  },\n});\n```\n\nIt might be that TS can't infer exact type of complex handler when passed to `addSchema` so you'll\nneed to do it manually\n\n```typescript\naddSchema(app, {\n  jsonSchema: {},\n  service: {\n    'GET /hello': myHandlers,\n    'GET /hello2': myHandlers as RequestHandler\u003cExtendedSchema, 'GET /hello2'\u003e['AsRoute'],\n  },\n});\n```\n\n### Annotating types\n\nThis library is using [typescript-json-schema](https://github.com/YousefED/typescript-json-schema) with custom\ntransforms for schema generation. All `@tjs` [annotations](https://github.com/YousefED/typescript-json-schema#annotations) can be used to fine-tune schema output\n\n- `@type` can be used to specify end type after using `toJSON, toString` methods of objects like `ObjectID` from MongoDB\n\n- since we use `typejescript-json-schema`: all known limitations are also inherited: - Records are not transformed correctly, use `{ [k: string]: string }` instead or hint with `@tjs`\n\n- [additionalProperties](https://json-schema.org/understanding-json-schema/reference/object#additionalproperties) are set to `false` by default\n  - use `{ [k: string]: T }` index to explicitly define type for `additionalProperties`\n  - or add `/** @additionalProperties true */` annotation to allow anything\n  - for details see:\n    - [default fastify AJV options](https://fastify.dev/docs/latest/Reference/Validation-and-Serialization/#validator-compiler)\n    - [removeAddtitional in AJV](https://ajv.js.org/options.html#removeadditional)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCoobaha%2Ftyped-fastify","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCoobaha%2Ftyped-fastify","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCoobaha%2Ftyped-fastify/lists"}