{"id":19528894,"url":"https://github.com/adonisjs/route-model-binding","last_synced_at":"2025-04-26T11:33:40.223Z","repository":{"id":50624537,"uuid":"496522359","full_name":"adonisjs/route-model-binding","owner":"adonisjs","description":"Add route model binding to your AdonisJS applications","archived":false,"fork":false,"pushed_at":"2024-05-09T22:45:48.000Z","size":1833,"stargazers_count":37,"open_issues_count":3,"forks_count":4,"subscribers_count":10,"default_branch":"develop","last_synced_at":"2024-09-20T02:18:12.100Z","etag":null,"topics":["first-party-package"],"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/adonisjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE.md","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":"thetutlage"}},"created_at":"2022-05-26T07:24:59.000Z","updated_at":"2024-08-29T23:43:04.000Z","dependencies_parsed_at":"2024-05-12T06:45:40.703Z","dependency_job_id":null,"html_url":"https://github.com/adonisjs/route-model-binding","commit_stats":{"total_commits":36,"total_committers":1,"mean_commits":36.0,"dds":0.0,"last_synced_commit":"56796e61d19354dc1776590976b40d719a5b6d90"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Froute-model-binding","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Froute-model-binding/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Froute-model-binding/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Froute-model-binding/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adonisjs","download_url":"https://codeload.github.com/adonisjs/route-model-binding/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224033010,"owners_count":17244520,"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":["first-party-package"],"created_at":"2024-11-11T01:20:36.627Z","updated_at":"2024-11-11T01:20:37.515Z","avatar_url":"https://github.com/adonisjs.png","language":"TypeScript","funding_links":["https://github.com/sponsors/thetutlage"],"categories":[],"sub_categories":[],"readme":"# Route model binding\n\u003e Bind the route parameters with Lucid models and automatically query the database\n\nRoute model binding is a neat way to remove one-liner Lucid queries from your codebase and use conventions to query the database during HTTP requests.\n\nIn the following example, we connect the route params `:post` and `:comments` with the arguments accepted by the `show` method.\n\n- The value of the first param from the URL will be used to query the first typed hinted model on the `show` method (i.e., Post).\n- Similarly, the value of the second param will be used to query the second typed hinted model (i.e., Comment).\n\n\u003e **Note**: The params and models are connected using the order they appear and not the name. This is because TypeScript decorators have no way to know the names of the arguments accepted by a method.\n\n```ts\n// Routes file\nRoute.get('posts/:post/comments/:comment', 'PostsController.show')\n```\n\n```ts\n// Controller\nimport Post from 'App/Models/Post'\nimport Comment from 'App/Models/Comment'\nimport { bind } from '@adonisjs/route-model-binding'\n\nexport default class PostsController {\n  @bind()\n  public async show({}, post: Post, comment: Comment) {\n    return { post, comment }\n  }\n}\n```\n\n\u003e ▶️ : **Are you a visual learner**? Checkout [these screencasts](https://learn.adonisjs.com/series/route-model-binding/introduction) to learn about Route model binding, its setup and usage.\n\n## Setup\nInstall the package from the npm registry as follows.\n\n```sh\nnpm i @adonisjs/route-model-binding\n\n# yarn lovers\nyarn add @adonisjs/route-model-binding\n```\n\nNext, configure the package by running the following ace command.\n\n```sh\nnode ace configure @adonisjs/route-model-binding\n```\n\nThe final step is to register the `RmbMiddleware` inside the `start/kernel.ts` file.\n\n```ts\nServer.middleware.register([\n  // ...other middleware\n  () =\u003e import('@ioc:Adonis/Addons/RmbMiddleware'),\n])\n```\n\n## Basic usage\nStart with the most basic example and then tune up the complexity level to serve different use cases.\n\nIn the following example, we will bind the `Post` model with the first parameter in the `posts/:id` route.\n\n```ts\nRoute.get('/posts/:id', 'PostsController.show')\n```\n\n```ts\nimport Post from 'App/Models/Post'\nimport { bind } from '@adonisjs/route-model-binding'\n\nexport default class PostsController {\n  @bind()\n  public async show({}, post: Post) {\n    return { post }\n  }\n}\n```\n\nThe params and models are matched in the order they are defined. So the first param in the URL matches the first type-hinted model in the controller method.\n\nThe match is not performed using the name of the controller method argument because TypeScript decorators cannot read them (so the technical limitation leaves us with the order-based matching only).\n\n## Changing the lookup key\nBy default, the model's primary key is used to find a matching row in the database. You can either change that globally or change it for just one specific route.\n\n### Change lookup key globally via model\nAfter the following change, the post will be queried using the `slug` property and not the primary key. In a nutshell, the `Post.findByOrFail('slug', value)` query is executed.\n\n```ts\nclass Post extends BaseModel {\n  public static routeLookupKey = 'slug'\n}\n```\n\n### Change the lookup key for a single route.\nIn the following example, we define the lookup key directly on the route enclosed with parenthesis.\n\n```ts\nRoute.get('/posts/:id(slug)', 'PostsController.show')\n```\n\n**Did you notice that our route now reads a bit funny?**\\\nThe param is written as `:id(slug)`, which does not translate well. Therefore, with Route model binding, we recommend using the model name as the route param because we are not dealing with the `id` anymore. We are fetching model instances from the database.\n\nFollowing is the better way to write the same route.\n\n```ts\nRoute.get('/posts/:post(slug)', 'PostsController.show')\n```\n\n## Change lookup logic\nYou can change the lookup logic by defining a static `findForRequest` method on the model itself. The method receives the following parameters.\n\n- `ctx` - The HTTP context for the current request\n- `param` - The parsed parameter. The parameter has the following properties.\n    - `param.name` - The normalized name of the parameter.\n    - `param.param` - The original name of the parameter defined on the route.\n    - `param.scoped` - If `true`, the parameter must be scoped to its parent model.\n    - `param.lookupKey` - The lookup key defined on the route or the model.\n    - `param.parent` - The name of the parent param.\n- `value` - The value of the param during the current request.\n\nIn the following example, we query only published posts. Also, make sure that this method either returns an instance of the model or raises an exception.\n\n```ts\nclass Post extends BaseModel {\n  public static findForRequest(ctx, param, value) {\n    const lookupKey = param.lookupKey === '$primaryKey' ? 'id' : param.lookupKey\n\n    return this\n      .query()\n      .where(lookupKey, value)\n      .whereNotNull('publishedAt')\n      .firstOrFail()\n  }\n}\n```\n\n## Scoped params\nWhen working with nested route resources, you might want to scope the second param as a relationship with the first param.\n\nA great example of this is finding a post comment by id and making sure that it is a child of the post mentioned within the same URL.\n\nThe `posts/1/comments/2` should return 404 if the post id of the comment is not `1`.\n\nYou can define scoped params using the `\u003e` greater than a sign or famously known as the [breadcrumb sign](https://www.smashingmagazine.com/2009/03/breadcrumbs-in-web-design-examples-and-best-practices/#:~:text=You%20also%20see%20them%20in,the%20page%20links%20beside%20it.)\n\n```ts\nRoute.get('/posts/:post/comments/:\u003ecomment', 'PostsController.show')\n```\n\n```ts\nimport Post from 'App/Models/Post'\nimport Comment from 'App/Models/Comment'\nimport { bind } from '@adonisjs/route-model-binding'\n\nexport default class PostsController {\n  @bind()\n  public async show({}, post: Post, comment: Comment) {\n    return { post, comment }\n  }\n}\n```\n\nFor the above example to work, you will have to define the `comments` as a relationship on the `Post` model. The type of the relationship does not matter.\n\n```ts\nclass Post extends BaseModel {\n  @hasMany(() =\u003e Comment)\n  public comments: HasMany\u003ctypeof Comment\u003e\n}\n```\n\nThe name of the relationship is looked up, converting the param name to `camelCase`. We will use both plural and singular forms to find the relationship.\n\n### Customizing relationship lookup\nBy default, the relationship is fetched using the lookup key of the bound child model. Effectively the following query is executed.\n\n```ts\nawait parent\n  .related('relationship')\n  .query()\n  .where(lookupKey, value)\n  .firstOrFail()\n```\n\nHowever, you can customize the lookup by defining the `findRelatedForRequest` method on the model (note, this is not a static method).\n\n```ts\nclass Post extends BaseModel {\n  public findRelatedForRequest(ctx, param, value) {\n    /**\n     * Have to do this weird dance because of\n     * https://github.com/microsoft/TypeScript/issues/37778\n     */\n    const self = this as unknown as Post\n    const lookupKey = param.lookupKey === '$primaryKey' ? 'id' : param.lookupKey\n\n    if (param.name === 'comment') {\n      return self\n      .related('comments')\n      .query()\n      .where(lookupKey, value)\n      .firstOrFail()\n    }\n  }\n}\n```\n\n## Unbound params\nYou will often have parameters that are raw values and cannot be tied to a model. In the following example, the `version` is a regular string value and not backed using the database.\n\n```ts\nRoute.get(\n  '/api/:version/posts/:post',\n  'PostsController.show'\n)\n```\n\nYou can represent the `version` as a string on the controller method, and we will perform no database lookup. For example:\n\n```ts\nimport Post from 'App/Models/Post'\nimport { bind } from '@adonisjs/route-model-binding'\n\nclass PostsController {\n  @bind()\n  public async show({}, version: string, post: Post) {}\n}\n```\n\nSince the route params and the controller method arguments are matched in the same order they are defined, you will always have to type-hint all the parameters.\n\n\u003cbr /\u003e\n\u003chr\u003e\n\n![](https://cdn.jsdelivr.net/gh/thetutlage/static/sponsorkit/sponsors.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadonisjs%2Froute-model-binding","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadonisjs%2Froute-model-binding","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadonisjs%2Froute-model-binding/lists"}