@front-commerce/remix
This package contains Front-Commerce implementations related to Remix.
Installation
First ensure you have installed the package:
$ pnpm install @front-commerce/remix@latest
Type Safety
You can get type safety over the network for your loaders with LoaderFunctionArgs and for your actions with ActionFunctionArgs.
To ensure that the typed frontCommerce context is in the arguments, you can add
the @front-commerce/remix reference to your remix.env.d.ts file.
/// <reference types="@remix-run/dev" />
/// <reference types="@remix-run/node" />
/// <reference types="@front-commerce/remix" />
Front-Commerce Context
To ensure the Front-Commerce Context is available in your loaders and actions,
you need to add the createFrontCommerceContext to your server via the
getLoadContext, to do
this, update your
server.ts
import { createFrontCommerceContext } from "@front-commerce/remix";
import { createRemixRequest } from "@remix-run/express/dist/server";
app.all(
"*",
createRequestHandler({
build: require(BUILD_DIR),
mode: process.env.NODE_ENV,
getLoadContext(request, response) {
const remixRequest = createRemixRequest(request, response);
return {
frontCommerce: createFrontCommerceContext(remixRequest),
};
},
})
);
assertMethod
Available from @front-commerce/remix:
import { assertMethod } from "@front-commerce/remix";
Guards a Remix action (or loader) against unsupported HTTP methods. When the
request method is not one of the allowed methods, it throws a uniform
405 Method Not Allowed response with an Allow header listing the accepted
methods and a JSON body { errorMessage: "Method not allowed" } — the response
is the same as if it had been returned, because Remix converts thrown responses
into HTTP responses as-is.
The Allow header reflects the methods passed to the helper — the handler's
vocabulary, not the whole resource's: on a route that also exports a loader,
GET/HEAD are served by the loader and don't need to be listed. Per RFC 9110
§9.3.2, when "GET" is allowed, HEAD is accepted and advertised in Allow
too, so guarding a loader never breaks HEAD requests.
import { assertMethod } from "@front-commerce/remix";
export const action = async ({ request }) => {
assertMethod(request, "POST");
// Several methods: assertMethod(request, ["POST", "DELETE"]);
// …
};
@front-commerce/remix/node
@front-commerce/remix/react
We exposed new methods in the @front-commerce/remix to replace the methods
from @remix-run which allows for better type safety:
These methods are used the same as the ones from @remix-run with the exception
of useApiFetcher, for example:
import { useLoaderData } from "@remix-run/react";
import { useLoaderData } from "@front-commerce/remix/react";
import { json } from "@remix-run/node";
import { json } from "@front-commerce/remix/node";
const loader = () => {
return json({
foo: "bar" as const
date: new Date()
});
};
export default function MyRoute() {
const data = useLoaderData<typeof loader>();
return (
<div>
{data.foo} // data will be typed as { foo: "bar", date: string } instead of SerializeObject<UndefinedToOptional<{ foo: "bar", date: string }>>
</div>
);
}
In the above example the data will be typed as
{ foo: "bar", date: string }
Instead of the standard remix SerializeObject type:
SerializeObject<UndefinedToOptional<{ foo: "bar"; date: string }>>;
useApiFetcher
The useApiFetcher is an optimised fetcher which allows to deduplicate
client-side calls to the same /api endpoint. It is designed to replace the
load method from useFetcher for API calls.
Here is an example of how to change from useFetcher to useApiFetcher:
- useFetcher
- useApiFetcher
import type { loader } from "./api.cart";
import { useFetcher } from "@front-commerce/remix/react";
export default function MyRoute() {
const fetcher = useFetcher<typeof loader>();
useEffect(() => {
if (!fetcher.data && fetcher.state !== "loading") {
fetcher.load("/api/cart");
}
}, [fetcher]);
if (fetcher.state === "loading") {
return <div>Loading...</div>;
}
const data = fetcher.data;
return (
<div>
Cart Id: {data.cart.id}
Cart Qty: {data.cart.items_qty}
</div>
);
}
import type { loader } from "./api.cart";
import { useApiFetcher } from "@front-commerce/remix/react";
export default function MyRoute() {
const { data, loading } = useApiFetcher<typeof loader>("/api/cart");
if (loading) {
return <div>Loading...</div>;
}
return (
<div>
Cart Id: {data.cart.id}
Cart Qty: {data.cart.items_qty}
</div>
);
}
Here is an example of 5 concurrent calls to the same /api/cart endpoint:
As you can see with the useFetcher it will call the API for each component,
with the useApiFetcher it will only called the API once and share the data
between the components. This also works when using the
useRevalidator
hook.
useApiFetcherPromise
Just like useApiFetcher, useApiFetcherPromise allows you to retrieve
optimised data from /api endpoints. The only difference is that it returns a
promise instead of the actual data. Use it when you need to process the data in
a more "synchronous" way, without depending on fetcher states.
Here is an example of how to use useApiFetcherPromise:
import type { loader } from "./api.cart";
import { useApiFetcherPromise } from "@front-commerce/remix/react";
export default function MyRoute() {
const doSomething = useApiFetcherPromise<typeof loader>("/api/do-something");
return (
<div>
<button
onClick={() =>
doSomething().then((result) =>
console.log("Something has been done!", result)
)
}
>
Do something !
</button>
</div>
);
}
CLI
translate
Extract translations from your application sources
Usage
$ front-commerce translate <path> --locale <locale>
Options
--help, -h Displays this message --locale Output locale for the translations
codegen
Run the
graphql-codegen
command
Usage
$ front-commerce codegen
Options
--help, -h Displays this message --watch, -w Watch for changes and regenerate --verbose, -v Verbose logging for debugging purposes
find-unused-style-variables
Checks the application for unused style variables.
Usage
$ front-commerce find-unused-style-variables <path/to/your/project> [second/path/to/your/project] ...
Options
--help, -h Displays this message
migrate
Migrate your project to a new version of Front-Commerce
Usage
$ front-commerce migrate <target>
Options
--help, -h Displays this message --dry, -d Run the migration without changing any file --verbose Show more information about the migration
worker
Runs the Server Side Events worker
instance.
This should be added as a script in the package.json
"scripts": {
"worker": "front-commerce worker"
}