{"id":15065623,"url":"https://github.com/leancodepl/ts-routes","last_synced_at":"2025-04-10T13:32:29.552Z","repository":{"id":42706786,"uuid":"269367357","full_name":"leancodepl/ts-routes","owner":"leancodepl","description":"Strongly typed parameterized routing paths","archived":false,"fork":false,"pushed_at":"2023-09-12T14:35:41.000Z","size":487,"stargazers_count":47,"open_issues_count":16,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-26T14:54:07.042Z","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":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leancodepl.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":"2020-06-04T13:29:30.000Z","updated_at":"2024-12-10T10:51:56.000Z","dependencies_parsed_at":"2024-10-13T01:21:42.175Z","dependency_job_id":"ab8abc51-1742-4f79-bdf4-0057e5e80d4a","html_url":"https://github.com/leancodepl/ts-routes","commit_stats":{"total_commits":33,"total_committers":5,"mean_commits":6.6,"dds":0.3939393939393939,"last_synced_commit":"f25bcf95213e0acebca0813144bdb7ae61abf5af"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fts-routes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fts-routes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fts-routes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leancodepl%2Fts-routes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leancodepl","download_url":"https://codeload.github.com/leancodepl/ts-routes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248225707,"owners_count":21068078,"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-09-25T00:43:25.681Z","updated_at":"2025-04-10T13:32:29.528Z","avatar_url":"https://github.com/leancodepl.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ts-routes\n\n[![npm](https://img.shields.io/npm/v/ts-routes)](https://www.npmjs.com/package/ts-routes)\n[![Actions Status](https://github.com/leancodepl/ts-routes/workflows/build/badge.svg)](https://github.com/leancodepl/ts-routes/actions)\n\nHelper library for constructing strongly typed parameterized routing paths. It prevents you from passing hardcoded\nstrings with routes across the app.\n\nts-routes is independent on routing libraries and can be used together with e.g. React Router DOM or Vue Router.\n\n## Installation\n\n```\nnpm install ts-routes\nyarn add ts-routes\n```\n\n## Quick start\n\n```ts\nimport { createRouting, number, query, segment, uuid } from 'ts-routes';\n\nconst routes = createRouting({\n    products: segment`/products`,\n    users: segment`/users/${number('userId')}`,\n    items: {\n        ...segment`/items`,\n        query: {\n            filter: query()\n        }\n        children: {\n            item: segment`/${uuid('itemId')}`,\n        },\n    },\n});\n\nroutes.products(); // '/products'\nroutes.products.pattern // '/products'\n\nroutes.users({ userId: '10' }) // '/users/10'\nroutes.users.pattern // '/users/:userId([0-9]+)\n\nroutes.items({}, { filter: 'new' }) // '/items?filter=new'\nroutes.items.pattern // '/items'\n\nroutes.items.item({ itemId: '12d66718-e47c-4a2a-ad5b-8897def2f6a7' }) // '/items/12d66718-e47c-4a2a-ad5b-8897def2f6a7'\nroutes.items.item.pattern // `/items/:itemId(${uuidRegex})`\n```\n\n## Usage\n\n### Routing\n\nTo use strongly typed paths, you first have to create the routing object by calling `createRouting` and providing an\nobject defining segments. Segments represent single routing paths and are implemented as tagged template literals:\n\n```ts\nconst routes = createRouting({\n    users: segment`/users`\n});\n```\n\nSecond parameter to `createRouting` is `qs` configuration. You can extend/alter `ts-routes` functionality by providing configuration to `qs`. For example you can change array formatting and delimiter. For details on configuration please refer to [`qs` documentation](https://github.com/ljharb/qs).\n\n### Parameters\n\nYou can define route params (i.e. parts of the path that are variable) by interpolating the `arg` function inside a\nsegment:\n\n```ts\nsegment`/users/${arg(\"userId\")}`;\n```\n\nThis will enable you to create paths like `/users/1` or `/users/username`.\n\nBy default route parameters are treated as required. You can make them optional by providing extra configuration. It is\nalso possible to limit possible parameter values by passing a regex string. While trying to create a route which doesn't\nsatisfy the pattern, an exception will be thrown.\n\n```ts\nsegment`/users/${arg(\"userId\", {\n    optionality: \"optional\",\n    pattern: \"[0-9]\",\n})}`;\n```\n\nWhen creating a route, path parameters can be passed in the first argument:\n\n```ts\nroutes.users({ userId: \"10\" });\n```\n\nThere are some predefined convenience parameter types provided:\n\n-   `string(name: string, optionality?: \"optional\" | \"required\" = \"required\")` for plain strings\n-   `number(name: string, optionality?: \"optional\" | \"required\" = \"required\")` for number strings\n-   `uuid(name: string, optionality?: \"optional\" | \"required\" = \"required\")` for UUID strings\n\n### Query string\n\nQuery string parameters can be specified by adding `query` property to the route description. The `query` function\nexpects an object where keys are names of parameters and values specify whether those params are required in the path.\n\n```ts\n{\n    ...segment`/product`,\n    query: {\n        productId: query(\"required\"),\n        details: query(\"optional\")\n    }\n}\n```\n\nThe above segment defines a path which expects the `productId` URL param and the optional `details` URL param.\n\nWhen creating a route query strings can be passed in the second argument:\n\n```ts\nroutes.products(\n    {},\n    {\n        productId: \"10\",\n        details: \"false\",\n    },\n);\n```\n\nwhich will return `/product?details=false\u0026productId=10`.\n\n`qs` by default supports also objects and arrays when stringifying and parsing query string.\n\n```ts\nconst parameters = routes.products.parseQuery(queryString)\n\n// this will yield given parameters with given type\n\ntype Parameters = {\n    productId: string | string[],\n    details?: string | string[],\n}\n```\n\nFor objects you need to specify your value type when defining routes:\n\n```ts\nimport { createRouting, number, query, uuid } from 'ts-routes';\n\ntype ProductDetails = { name: string, price: string };\n\nconst routes = createRouting({\n    products: {\n        ...segment`/product`,\n        query: {\n            productId: query(\"required\"),\n            details: query\u003cProductDetails, \"optional\"\u003e(\"optional\")\n        }\n    }\n});\n\nconst parameters = routes.products.parseQuery(queryString)\n\n// which will yield\n\ntype Parameters = {\n    productId: string | string[],\n    details?: ProductDetails | ProductDetails[],\n}\n```\n\nAdditionaly you can override `qs` stringify and parse option directly on each route:\n```ts\n    routes.products(undefined, { productId: \"10\" }, overrideStringifyOptions);\n\n    routes.products.parse(queryString, overrideParseOptions);\n```\n\n### Nested routes\n\nRoutes can be nested by providing an optional `children` property to segments:\n\n```ts\nconst routes = createRouting({\n    parent: {\n        ...segment`/parent`,\n        children: {\n            child: segment`/child`,\n        },\n    },\n} as const);\n```\n\nChild routes are attached to the parent route object so that to construct a child route you can call\n`routes.parent.child()` (which will return `/parent/child`).\n\nRoutes can be deeply nested and child routes will include all required and optional route parameters and query string\nparameters from parent routes.\n\n### Patterns\n\nWhile creating a routing, alongside path string generators, patterns for those paths compatible with\n[path-to-regexp](https://github.com/pillarjs/path-to-regexp) are generated. You can access them via the `pattern`\nproperty:\n\n```\nroutes.products.pattern\n```\n\nThose patterns are useful for integration with routing libraries which support\n[path-to-regexp](https://github.com/pillarjs/path-to-regexp)-style syntax (e.g. React Router DOM, Vue Router).\n\n### React Router DOM\n\nYou can use patterns for defining routes:\n\n```tsx\n\u003cRoute exact component={ProductsPage} path={routes.products.pattern} /\u003e\n```\n\nWith React it's also useful to add some helper types which can be used for typing routing props for components:\n\n```ts\nimport { FunctionComponent } from \"react\";\nimport { RouteComponentProps } from \"react-router-dom\";\nimport { PathParamsFor } from \"ts-routes\";\n\ntype PageProps\u003cTPathParams extends (...args: any[]) =\u003e string\u003e = RouteComponentProps\u003cPathParamsFor\u003cTPathParams\u003e\u003e;\n\ntype PageComponent\u003cTPathParams extends (...args: any[]) =\u003e string\u003e = FunctionComponent\u003cPageProps\u003cTPathParams\u003e\u003e;\n```\n\nWhich you can then use like so:\n\n```tsx\ntype ProductsPageProps = PageProps\u003ctypeof routes.products\u003e;\n\nconst ProductPage: PageComponent\u003ctypeof routes.products\u003e = ({\n    match: {\n        params: { productId },\n    },\n}) =\u003e \u003cdiv\u003e{productId}\u003c/div\u003e;\n```\n\n### Vue Router\n\nYou can use patterns for defining routes:\n\n```ts\nconst router = new VueRouter({\n    routes: [\n        {\n            path: routes.products.pattern,\n            component: ProductsPage,\n        },\n    ],\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleancodepl%2Fts-routes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleancodepl%2Fts-routes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleancodepl%2Fts-routes/lists"}