{"id":22880957,"url":"https://github.com/beermoneydev/nest-remix","last_synced_at":"2025-05-07T02:42:53.333Z","repository":{"id":65552286,"uuid":"594168918","full_name":"BeerMoneyDev/nest-remix","owner":"BeerMoneyDev","description":"An interop layer between NestJS and Remix","archived":false,"fork":false,"pushed_at":"2023-10-27T16:16:28.000Z","size":247,"stargazers_count":57,"open_issues_count":3,"forks_count":2,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-31T05:32:30.751Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BeerMoneyDev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"license.md","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null},"funding":{"custom":["https://www.linkedin.com/in/kerryritter","https://twitter.com/kerryritter"]}},"created_at":"2023-01-27T19:03:35.000Z","updated_at":"2025-01-17T14:57:34.000Z","dependencies_parsed_at":"2023-02-15T19:45:33.715Z","dependency_job_id":"7e8c636f-fa10-482f-a3d7-e76f6ac95c26","html_url":"https://github.com/BeerMoneyDev/nest-remix","commit_stats":{"total_commits":81,"total_committers":5,"mean_commits":16.2,"dds":0.3580246913580247,"last_synced_commit":"73051d854380f95ee6effd7fe697e5f43502756c"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeerMoneyDev%2Fnest-remix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeerMoneyDev%2Fnest-remix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeerMoneyDev%2Fnest-remix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BeerMoneyDev%2Fnest-remix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BeerMoneyDev","download_url":"https://codeload.github.com/BeerMoneyDev/nest-remix/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252802607,"owners_count":21806537,"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-12-13T17:28:48.347Z","updated_at":"2025-05-07T02:42:53.310Z","avatar_url":"https://github.com/BeerMoneyDev.png","language":"TypeScript","funding_links":["https://www.linkedin.com/in/kerryritter","https://twitter.com/kerryritter"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003enest-remix\u003c/h1\u003e\n\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003cstrong\u003eAn interop layer between NestJS and Remix\u003c/strong\u003e\n\u003c/div\u003e\n\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-remix\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/nest-remix.svg\" alt=\"NPM Version\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-remix\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/nest-remix.svg\" alt=\"Package License\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-remix\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/nest-remix.svg\" alt=\"NPM Downloads\" /\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n# Features\n\n- Serving of a Remix build through a custom NestJS server implementation.\n- Support of using NestJS @Injectable() services as Remix `action` and `loader` functions via `wireBacken\n- Support of NestJS pipes in `action` and `loader` into backend services\n- Support of [NestJS versioning](https://docs.nestjs.com/techniques/versioning) routing controllers\n- Support of [NestJS global prefix](https://docs.nestjs.com/faq/global-prefix) in app module\n\n# Overview\n\nCheck out this video on YouTube about NestJS, Remix, and the goal of `nest-remix`: \u003ca href=\"https://youtu.be/GGqr20kp28E\"\u003eSupercharged NestJS React Rendering with Remix\u003c/a\u003e\n\n# How To Use\n\n## Install\n\n#### Using NestJS Schematics\n\nThis will:\n\n- Install `nest-remix` and its NestJS, Remix, and React dependencies.\n- (Optionally) Update the tsconfig.json with a known working configuration to support NestJS and Remix.\n- (Optionally) Update app.module to use @RemixModule()\n- (Optionally) Add the required NPM scripts\n- (Optionally) Add versioning to the app module\n\n```bash\nnest add nest-remix\n```\n\n#### Manually\n\nDetails coming soon.\n\n## Basic Usage\n\n#### Setup and Configuration\n\nTo add the interop layer, import the `RemixModule` from `nest-remix` into your NestJS application module. The RemixModule takes a configuration object with the following properties\n\n- `publicDir` - The directory where your Remix public assets reside. This is the directory that Remix will build into.\n- browserBuildDir` - The directory where your Remix browser build is located. This is the directory Remix will build into.\n- serverBuildDir` - The directory where your Remix server build is located. This is the directory Remix will build into.\n\n```ts\n// before\nimport { AppService } from './app.service';\nimport { AppController } from './app.controller';\nimport { Module } from '@nestjs/common';\n\n@Module({\n  controllers: [AppController],\n  providers: [AppService],\n  exports: [AppService],\n})\nexport class AppModule {}\n\n// after\nimport * as path from 'path';\n\nimport { AppService } from './app.service';\nimport { AppController } from './app.controller';\nimport { Module } from '@nestjs/common';\nimport { RemixModule } from 'nest-remix';\n\n@Module({\n  imports: [\n    RemixModule.forRoot({\n      publicDir: path.join(process.cwd(), 'public'),\n      browserBuildDir: path.join(process.cwd(), 'build/'),\n    }),\n  ],\n  controllers: [AppController],\n  providers: [AppService],\n  exports: [AppService],\n})\nexport class AppModule {}\n```\n\n#### Adding Remix routes\n\nAdd all your Remix routes to `src/app/routes` as you would in a typical Remix application.\n\n#### Using NestJS services as `action` and `loader` functions\n\nThe `wireLoader` and `wireAction` functions connect to the RemixModule-decorated module to get providers. By supplying these functions with the type to be used as the backend, nest-remix will route the request appropriately given the `@Loader()` and `@Action()` decorators. Services can be injected into the backend class as expected given their module hierarchy.\n\nIt is required to create a new file as the NestJS decorators will attempt to execute as client-side code otherwise, breaking your build. As such, you will need two files for a route now (sorry!) - `{your-route}.tsx` and `{your-route}.server.ts` (you can name it whatever you want, but this is the recommended naming convention).\n\n**Note: backends must be provided/exported to be accessible by the RemixModule-decorated module.**\n\n```tsx\n// src/app/routes/hello-world.tsx\nimport type { ActionFunction, LoaderFunction } from '@remix-run/node';\nimport { Form, useActionData, useLoaderData } from '@remix-run/react';\nimport { wireAction, wireLoader } from 'nest-remix/core.server';\nimport { HelloWorldBackend } from './hello-world.server';\n\nexport const loader: LoaderFunction = (args) =\u003e\n  wireLoader(HelloWorldBackend, args);\n\nexport const action: ActionFunction = (args) =\u003e\n  wireAction(HelloWorldBackend, args);\n\nexport default function HelloWorld() {\n  const { message } = useLoaderData\u003cHelloWorldBackend['getMessage']\u003e();\n  const actionData = useActionData\u003c\n    HelloWorldBackend['setMessage'] | HelloWorldBackend['setMessageFallback']\n  \u003e();\n\n  return (\n    \u003cdiv style={{ fontFamily: 'system-ui, sans-serif', lineHeight: '1.4' }}\u003e\n      \u003ch1\u003eWelcome to Remix\u003c/h1\u003e\n      \u003cdiv style={{ marginTop: 20 }}\u003e{actionData?.newMessage || message}\u003c/div\u003e\n      \u003cfieldset style={{ marginTop: 20 }}\u003e\n        \u003clegend\u003eUpdate the message\u003c/legend\u003e\n        \u003cForm method=\"post\"\u003e\n          \u003cinput type=\"text\" name=\"message\" defaultValue={''} /\u003e\n          \u003cbutton\u003ePost update\u003c/button\u003e\n        \u003c/Form\u003e\n        \u003cForm method=\"put\"\u003e\n          \u003cinput type=\"text\" name=\"message\" defaultValue={''} /\u003e\n          \u003cbutton\u003ePut update\u003c/button\u003e\n        \u003c/Form\u003e\n      \u003c/fieldset\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n```ts\n// src/app/routes/hello-world.server.ts\nimport { Body, Injectable, ParseIntPipe, Query } from '@nestjs/common';\nimport { LoaderFunctionArgs } from '@remix-run/node';\nimport { Action, Loader, RemixArgs } from 'nest-remix';\nimport { AppService } from './app.service.ts';\n\n@Injectable()\nexport class HelloWorldBackend {\n  constructor(private readonly appService: AppService);\n\n  @Loader()\n  getMessage(\n    @Query('defaultMessage') defaultMessage: string,\n    @Query('counter', ParseIntPipe) _counter: number,\n    @RemixArgs() _remixArgs: LoaderFunctionArgs\n  ) {\n    return { message: defaultMessage || this.appService.getDefaultMessage() };\n  }\n\n  @Action()\n  async setMessageFallback(@Body() body: { message: string }) {\n    return { newMessage: body.message + ' [POST, DELETE]' };\n  }\n\n  @Action.Put()\n  async setMessage(@Body() body: { message: string }) {\n    return { newMessage: body.message + ' [PUT]' };\n  }\n}\n```\n\n##### `action` Routing\n\nThe `@Action()` decorator will capture all requests to the wired `action` function. Additional routing is possible by using `@Action.Post()`, `@Action.Put()`, and `@Action.Delete()`. It will always fall back to `@Action()` if an HTTP verb is not supplied.\n\n##### Available routing decorators\n\n###### Function decorators\n\n- `@Loader()` - The loader for a view backend\n- `@Action()` - The action for a view backend\n- `@Action.Post()` - The action for a view backend if the request method is POST\n- `@Action.Put()` - The action for a view backend if the request method is PUT\n- `@Action.Patch()` - The action for a view backend if the request method is PATCH\n- `@Action.Delete()` - The action for a view backend if the request method is DELETE\n\n###### Parameter decorators\n\n- `@RemixArgs()` - Injects the loader or action args\n- `@Query()` - Injects a query parameter by name (pipes are supported)\n- `@Param()` - Injects a route URL parameter by name (pipes are supported)\n- `@Body()` - Injects the request body (pipes are supported)\n- Support coming soon: `@Headers`, `@Session`, `@File`, `@Files`, `@Host`, `@Ip`\n\n##### Accessing the Remix args\n\n`nest-remix` provides a custom decorator, `@RemixArgs()`. This provides the `LoaderArgs` or `ActionArgs` depending on the type of function.\n\n##### NestJS pipes in `@Action()` and `@Loader()` functions\n\nPipes should work as expected. They are applied to the NestJS request object, NOT the Remix request. But, the Remix request object is accessible via `@RemixArgs()`.\n\n## How it works\n\n`@RemixModule` appends a custom controller (`RemixController`) which handles the `@All('*')` route at the root. This controller is at the end of the pipeline, so NestJS will attempt to server the request before it falls back to Remix. During this process, we use the NestJS module ref to provide injectable services for Remix backends.\n\n## Important notes\n\n### Use `nest-remix/core.server` in your route files\n\nAs described in the remix.run \"gotcha's\" page, it is important to use `.server`-suffixed files from your routes to avoid server-side rendering attempts on your client-side code. A fortunate side-effect of `nest-remix` is that this pattern allows you to funnel all server-side code through `nest-remix/core.server` and `{your-route}.server.ts`, inherently protecting you.\n\n# Stay In Touch\n\n- Author - [Kerry Ritter](https://twitter.com/kerryritter) and BeerMoneyDev\n\n## License\n\nnest-remix is MIT licensed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeermoneydev%2Fnest-remix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeermoneydev%2Fnest-remix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeermoneydev%2Fnest-remix/lists"}