Server Adapters

Idiomatic Remix apps can generally be deployed anywhere because Remix adapt’s the server’s request/response to the Web Fetch API. It does this through adapters. We maintain a few adapters:

  • @remix-run/express
  • @remix-run/architect
  • @remix-run/vercel
  • @remix-run/netlify
  • @remix-run/cloudflare-workers

These adapters are imported into your server’s entry and is not used inside of your Remix app itself.

If you initialized your app with npx create-remix@latest with something other than the built-in Remix App Server, you will note a server/index.js file that imports and uses one of these adapters.

If you’re using the built-in Remix App Server, you don’t interact with this API

Each adapter has the same API. In the future we may have helpers specific to the platform you’re deploying to.

createRequestHandler

Creates a request handler for your server to serve the app. This is the ultimate entry point of your Remix application.

  1. const {
  2. createRequestHandler
  3. } = require("@remix-run/{adapter}");
  4. createRequestHandler({ build, getLoadContext });

Here’s a full example with express:

  1. const express = require("express");
  2. const {
  3. createRequestHandler
  4. } = require("@remix-run/express");
  5. const app = express();
  6. // needs to handle all verbs (GET, POST, etc.)
  7. app.all(
  8. "*",
  9. createRequestHandler({
  10. // `remix build` and `remix dev` output files to a build directory, you need
  11. // to pass that build to the request handler
  12. build: require("./build"),
  13. // return anything you want here to be available as `context` in your
  14. // loaders and actions. This is where you can bridge the gap between Remix
  15. // and your server
  16. getLoadContext(req, res) {
  17. return {};
  18. }
  19. })
  20. );

Here’s an example with Architect (AWS):

  1. const {
  2. createRequestHandler
  3. } = require("@remix-run/architect");
  4. exports.handler = createRequestHandler({
  5. build: require("./build")
  6. });

Here’s an example with Vercel:

  1. const {
  2. createRequestHandler
  3. } = require("@remix-run/vercel");
  4. module.exports = createRequestHandler({
  5. build: require("./build")
  6. });

Here’s an example with Netlify:

  1. const path = require("path");
  2. const {
  3. createRequestHandler
  4. } = require("@remix-run/netlify");
  5. const BUILD_DIR = path.join(process.cwd(), "netlify");
  6. function purgeRequireCache() {
  7. // purge require cache on requests for "server side HMR" this won't let
  8. // you have in-memory objects between requests in development,
  9. // netlify typically does this for you, but we've found it to be hit or
  10. // miss and some times requires you to refresh the page after it auto reloads
  11. // or even have to restart your server
  12. for (const key in require.cache) {
  13. if (key.startsWith(BUILD_DIR)) {
  14. delete require.cache[key];
  15. }
  16. }
  17. }
  18. exports.handler =
  19. process.env.NODE_ENV === "production"
  20. ? createRequestHandler({ build: require("./build") })
  21. : (event, context) => {
  22. purgeRequireCache();
  23. return createRequestHandler({
  24. build: require("./build")
  25. })(event, context);
  26. };

Here’s an example with the simplified Cloudflare Workers API:

  1. import { createEventHandler } from "@remix-run/cloudflare-workers";
  2. import * as build from "../build";
  3. addEventListener("fetch", createEventHandler({ build }));

Here’s an example with the lower level Cloudflare Workers API:

  1. import {
  2. createRequestHandler,
  3. handleAsset
  4. } from "@remix-run/cloudflare-workers";
  5. import * as build from "../build";
  6. const handleRequest = createRequestHandler({ build });
  7. const handleEvent = async (event: FetchEvent) => {
  8. const response = await handleAsset(event, build);
  9. if (!response) {
  10. response = await handleRequest(event);
  11. }
  12. return response;
  13. };
  14. addEventListener("fetch", event => {
  15. try {
  16. event.respondWith(handleEvent(event));
  17. } catch (e: any) {
  18. if (process.env.NODE_ENV === "development") {
  19. event.respondWith(
  20. new Response(e.message || e.toString(), {
  21. status: 500
  22. })
  23. );
  24. }
  25. event.respondWith(
  26. new Response("Internal Error", { status: 500 })
  27. );
  28. }
  29. });