{"id":16295258,"url":"https://github.com/seia-soto/typescript-fastify-boilerplate","last_synced_at":"2026-05-09T16:44:32.012Z","repository":{"id":103812053,"uuid":"411735429","full_name":"seia-soto/typescript-fastify-boilerplate","owner":"seia-soto","description":"A new awesome project using Fastify, TAP, and TypeScript! ✨","archived":false,"fork":false,"pushed_at":"2021-10-31T19:18:18.000Z","size":1737,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-15T07:19:50.041Z","etag":null,"topics":["backend","boilerplate","fastify","typescript"],"latest_commit_sha":null,"homepage":"","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/seia-soto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2021-09-29T15:46:26.000Z","updated_at":"2024-10-13T01:15:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"c1153702-09cf-4712-a096-07f700502e65","html_url":"https://github.com/seia-soto/typescript-fastify-boilerplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seia-soto%2Ftypescript-fastify-boilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seia-soto%2Ftypescript-fastify-boilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seia-soto%2Ftypescript-fastify-boilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seia-soto%2Ftypescript-fastify-boilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seia-soto","download_url":"https://codeload.github.com/seia-soto/typescript-fastify-boilerplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248045266,"owners_count":21038555,"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":["backend","boilerplate","fastify","typescript"],"created_at":"2024-10-10T20:18:13.104Z","updated_at":"2025-10-08T07:43:48.129Z","avatar_url":"https://github.com/seia-soto.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# New TypeScript Fastify Project\n\nA new awesome project! ✨\n\n## Table of Contents\n\n- [Project](#project)\n  - [Conventions](#conventions)\n- [LICENSE](#license)\n\n----\n\n# Project\n\nThis project includes following packages:\n\n- Fastify\n  - fastify-no-additional-properties\n- TAP\n  - AJV2019\n  - ajv-formats\n- TypeScript\n  - ts-node with swc integration\n\n```\n📦 src\n ┣ 📂 api\n ┃ ┣ 📂 v1\n ┃ ┃ ┣ 📜 health.ts\n ┃ ┃ ┗ 📜 index.ts\n ┃ ┗ 📜 index.ts\n ┣ 📂 replies\n ┃ ┣ 📜 health.ts\n ┃ ┣ 📜 index.ts\n ┃ ┗ 📜 utils.ts\n ┣ 📂 types\n ┃ ┗ 📜 fastify-no-additional-properties.d.ts\n ┣ 📜 index.ts\n ┣ 📜 preferences.sample.ts\n ┗ 📜 preferences.ts (create for local environment)\n```\n\n## Conventions\n\nThere are some pratical convetions for you to improve productivity.\n\n### Response schema and consistency management\n\nTo test Fastify safe as possible and reduce duplicated codes while testing API, I recommend you to add schemes to `/src/replies`.\nIn this project, I created a sample `health` api schema to boost your understanding.\n\nThe following shows the content of `/src/replies/health.ts`, and you can see you can **type** the response schema via `typebox`.\n\n```typescript\nimport { Static, Type } from '@sinclair/typebox'\nimport { createReplyCallback, createSchema } from './utils'\n\nexport const HealthQuerySchema = createSchema(Type.Object({\n  time: Type.Integer()\n}))\nexport type THealthQuerySchema = Static\u003ctypeof HealthQuerySchema\u003e\n\nexport const queried = createReplyCallback\u003cTHealthQuerySchema\u003e(\n  'APP_HEALTH_QUERIED',\n  true\n)\n```\n\n`createSchema` creates definitive response schema for you and customizable from `/src/replies/utils.ts`.\nThis function creates consistent schema of response to improve productivity with clients such as front-ends and applications.\n\nBy default, all additional properties go though `payload` property.\n\n```typescript\n/**\n * Build schema dynamically setting reply payload\n *\n * @param innerSchema The schema of the reply payload\n * @returns Dynamically built schema added payload type\n */\nexport const createSchema = \u003cT extends TSchema\u003e(innerSchema: T): TObject\u003c{\n  code: TString,\n  success: TBoolean,\n  payload: T\n}\u003e =\u003e {\n  return Type.Object({\n    code: Type.String(),\n    success: Type.Boolean(),\n    payload: innerSchema\n  })\n}\n```\n\nUsing provided functions composed will enable **type checking** on response code at the time.\nGenerated `typebox` schemes and types will be applied to fastify routing and you can see red underlines if you not return a valid payload to `replies.health.queried` function composed with `createReplyCallback` function.\n\n\u003e `createReplyCallback` function is just a simple helper function for `createReply` function to dynamically inject additional properties into `payload` property of response.\n\n```typescript\nimport type { FastifyPluginCallback } from 'fastify'\nimport * as replies from '../../replies'\n\nexport const router: FastifyPluginCallback = (fastify, opts, done) =\u003e {\n  fastify.route\u003c{ Reply: replies.utils.TReplySchema | replies.health.THealthQuerySchema }\u003e({\n    method: 'GET',\n    url: '/',\n    schema: {\n      response: {\n        200: replies.health.HealthQuerySchema\n      }\n    },\n    handler: async () =\u003e {\n      return replies.health.queried({\n        time: Date.now()\n      })\n    }\n  })\n\n  done()\n}\n```\n\nNow, in testing, see what's happening:\n\n```typescript\ntest('health check', async (t: ITapTest) =\u003e {\n  const { statusCode, ...response } = await t.context.server.inject({\n    url: '/api/v1/health',\n    method: 'GET'\n  })\n  const body: replies.health.THealthQuerySchema = response.json()\n\n  t.equal(statusCode, 200, 'return a status code of 200')\n  t.ok(t.context.ajv.compile(replies.health.HealthQuerySchema)(body), 'return a valid format of response')\n})\n```\n\nYou really don't need to check every properties.\nJust provide valid schema from `/src/replies` module.\n\nBy using `replies` module, we can easily take productivity and reduce duplicated codes.\n\n### TAP `t.context` expansion\n\nWe commonly import things from source when testing our project.\nNot like common project, this is TypeScript project and we need to enable **type checking** on testing code too.\n\nIn this case, I already included what you need at common in test code.\nSee `/test/api/project.ts`, or see following as it is the part of the file.\n\n```typescript\nexport interface ITapContext {\n  server: FastifyInstance\n  ajv: Ajv\n}\nexport interface ITapTest extends TTapTest {\n  context: ITapContext\n}\n\nexport const context: Partial\u003cITapContext\u003e = {}\n```\n\nAdd your things to `ITapContext`, then things will be prepared and available to `t.context` by applying extended type.\nI used local variable `context` on `/test/api/project.ts` to manage it outside of TAP context.\n\n```typescript\nexport const beforeEachFn = async (t: ITapTest) =\u003e {\n  context.server ??= await instance({\n    logger: {\n      level: 'info',\n      prettyPrint: true\n    }\n  })\n  context.ajv ??= addFormats(new Ajv({}), [\n    'date-time',\n    'time',\n    'date',\n    'email',\n    'hostname',\n    'ipv4',\n    'ipv6',\n    'uri',\n    'uri-reference',\n    'uuid',\n    'uri-template',\n    'json-pointer',\n    'relative-json-pointer',\n    'regex'\n  ]).addKeyword('kind')\n    .addKeyword('modifier')\n\n  t.context = context as ITapContext\n}\n```\n\nNow, test with `ITapTest`:\n\n```typescript\ntest('health check', async (t: ITapTest) =\u003e {\n  ...\n})\n```\n\n### Application preferences on CI\n\nThere are many situations requires us to manage multiple environment.\nLike testing our code on CI.\n\nLet's take a look of current application structure.\n\n- **Actual preferences will be loaded** from `/src/preferences.ts`.\n- Actual preferences **is not available** when we clone this project.\n\nJust prepare another preferences file first.\nI'll name it to `preferences.ci.ts`.\n\n```ts\nexport = {\n  app: {\n    port: 5000\n  }\n}\n```\n\n\u003e We really don't need anything from that file as Fastify doesn't require to listen responses while testing but only for example.\n\n**Copy the CI preferences file to actual preferences file location** if `/src/preferences.ts` not available.\nWhy? It's not available after you clone the project.\n\nCI won't take up `/src/preferences.ts` as it is specified in `.gitignore` file and you can ensure the environment if `preferences.ts` file not available.\n\n### Keep things updated ASAP\n\nThe last things you need to check is the versions of your packages.\nCode will break? No, you need to update to check if code is breaking.\n\n- **Don't** let your code being **legacy**.\n- **Don't** afraid your code breaking by updating dependencies if you have a time to fix. At least better than **legacy**.\n- **Don't postpone changing to the faster library** unless you're entrepreneur or shipping time of application is more important.\n\nAfter cloning this as template repository, or starting with this project, **please run**: `yarn up \"**\"`\n\n\u003e The shell of Yarn berry is cross-platform. `\"**\"` won't break on your system.\n\n# LICENSE\n\nThis project is under MIT license and free to use for everyone.\n\nI love Fastify and its ecosystem, so created this boilderplate to increase usage of Fastify.\nAlso, I welcome adding credits my boilerplate helped your project and would happy to hear that.\n\n```\nMIT License Copyright 2021 HoJeong Go\n\nPermission is hereby granted, free of\ncharge, to any person obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice\n(including the next paragraph) shall be included in all copies or substantial\nportions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO\nEVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseia-soto%2Ftypescript-fastify-boilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseia-soto%2Ftypescript-fastify-boilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseia-soto%2Ftypescript-fastify-boilerplate/lists"}