{"id":16792510,"url":"https://github.com/ivanhofer/typesafe-api-endpoints","last_synced_at":"2025-04-10T23:51:57.147Z","repository":{"id":57383717,"uuid":"419211782","full_name":"ivanhofer/typesafe-api-endpoints","owner":"ivanhofer","description":null,"archived":false,"fork":false,"pushed_at":"2022-01-02T08:13:49.000Z","size":310,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-10T23:51:51.307Z","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/ivanhofer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-10-20T06:31:29.000Z","updated_at":"2024-03-11T04:28:35.000Z","dependencies_parsed_at":"2022-09-14T00:52:33.752Z","dependency_job_id":null,"html_url":"https://github.com/ivanhofer/typesafe-api-endpoints","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Ftypesafe-api-endpoints","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Ftypesafe-api-endpoints/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Ftypesafe-api-endpoints/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanhofer%2Ftypesafe-api-endpoints/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivanhofer","download_url":"https://codeload.github.com/ivanhofer/typesafe-api-endpoints/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248317706,"owners_count":21083528,"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":[],"created_at":"2024-10-13T08:46:12.585Z","updated_at":"2025-04-10T23:51:57.117Z","avatar_url":"https://github.com/ivanhofer.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# typesafe-api-endpoints\n\nThis is a proof of concept I have created to see how far we can take TypeScripts type inference to create a fully typesafe endpoints schema.\n\n**Result: It works quiet well! (with TypeScript version 4.4.x)**\n\n\u003e Note: I have used it in a [`SvelteKit`](https://kit.svelte.dev/) production environment for a few months but recently switched to [`tRPC`](https://github.com/trpc/trpc) because I currently have no time to maintain this project.\n\n## Usage:\n\n1. First you need to define a `Schema` containing all HTTP methods, it's endpoints, slugs, query params and return types.\n2. Then use the `Schema` to implement a `Server`-handler\n3. Call the endpoints from the `Client`\n\n### Schema\n\nWe can define a schema like:\n\n```ts\ntype Schema = CreateTypesafeApiEndpointsSchema\u003c{\n   GET: {\n      'products': Product[]\n      'products?limit': Product[]\n      'product/{id}': Product\n   }\n   POST: {\n      'product': (product: Product) =\u003e boolean\n   },\n   PUT: {\n      'product/{productId}': (product: Partial\u003cProduct\u003e) =\u003e boolean\n   }\n}\u003e\n```\n\n### Server\n\nAnd can use the `Schema` type for our `server-handler` that forces us to implement all methods and it's endpoints:\n\n```ts\nexport const handler: ServerHandler\u003cSchema, 'SvelteKit'\u003e = {\n   GET: {\n      'products': async () =\u003e {\n         return products\n      },\n      'products?limit': async ({ query: { limit = Infinity } }) =\u003e {\n         return products.slice(0, +limit)\n      },\n      'product/{id}': async ({ slugs: { id } }) =\u003e {\n         return products.find(({ id: productId }) =\u003e +id === productId)\n      }\n   },\n   POST: {\n      'product': async ({ body: product }) =\u003e {\n         // create logic\n         return true\n      }\n   },\n   PUT: {\n      'product/{productId}': async ({ slugs: { productId }, body: product }) =\u003e {\n         // update logic\n         return true\n      }\n   }\n}\n```\n\n### Client\n\nWe now can also create the `client`:\n\n```ts\nexport const api = createTypesafeApiEndpointsClient\u003cSchema\u003e('api-endpoint', fetch)\n```\n\nWhenever we use `api` a request is made to the endpoint we have provided as the first argument (in this case : `/api-endpoint`)\n\nWe can now call all endpoints from the `api`-object and get fully typed\n\n```ts\nconst loadData = async () =\u003e {\n   const response = await api.GET('product/{id}', { slugs: { id: '123' }, query: undefined })\n\n   if (response.error) {\n      throw response.error\n   }\n\n   return response.data\n}\n\nconst data = await loadData()\n// =\u003e 'data' is of type 'Product'\n```\n\n\u003e I could not get rid of needing to pass also `undefined` in some cases. I think the type definitions are to complex for TypeScript to resolve everything correctly.\n\n## Examples\n\nYou can take a look ath the [`examples`](https://github.com/ivanhofer/typesafe-api-endpoints/tree/main/examples) folder to see an implementation of this library\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanhofer%2Ftypesafe-api-endpoints","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivanhofer%2Ftypesafe-api-endpoints","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanhofer%2Ftypesafe-api-endpoints/lists"}