Add an express-like middleware stack to your remix loaders and actions!
yarn add remix-middleware
// ./app/middleware.server.ts
export const mdw = createMiddleware();
mdw.use(async (ctx, next) => {
console.log("middleware activated for", ctx.request.url);
await next();
console.log("middleware completed for", ctx.request.url);
});
mdw.use(mdw.routes());
// ./app/routes/posts/index.tsx
import { ActionFunction, LoaderFunction, Form, useLoaderData } from "remix";
import { mdw } from "~/middleware.server";
interface Post {
id: string;
title: string;
}
export const loader: LoaderFunction = (props) =>
mdw.run(props, (ctx) => {
// ctx.response is where the response object goes
ctx.response = [
{
id: "1",
title: "My First Post",
},
{
id: "2",
title: "A Mixtape I Made Just For You",
},
];
});
export const action: ActionFunction = (props) =>
mdw.run(props, async (ctx) => {
const body = await ctx.request.formData();
const post = { id: "3", title: body.get("title") };
ctx.response = post;
});
export default function Posts() {
const posts = useLoaderData<Post[]>();
return (
<div>
<h1>Posts</h1>
<div>
{posts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</div>
<div>
<Form method="post">
<p>
<label>
Title: <input name="title" type="text" />
</label>
</p>
<p>
<button type="submit">Create</button>
</p>
</Form>
</div>
</div>
);
}
Just have simple JSON that you are returning in a loader like in the example above?
export const loader: LoaderFunction = (props) =>
mdw.run(
props,
mdw.response([
{
id: "1",
title: "My First Post",
},
{
id: "2",
title: "A Mixtape I Made Just For You",
},
])
);
We built a middleware that will help interacting with remix-auth more streamlined.
- isAuthenticated - activates
authenticator.isAuthenticated
Setting up remix-auth
// ./app/user.ts
export interface User {
id: string;
email: string;
}
// ./app/authenticator.ts
import { Authenticator } from 'remix-auth';
import { sessionStorage } from "./session";
import type { User } from './user';
export const authenticator = new Authenticator<User>(sessionStorage);
Create middleware for your needs
// ./app/middleware.server.ts
import { createMiddleware, AuthCtx, isAuthenticated } from 'remix-middleware';
import { authenticator } from './authenticator';
import type { User } from './user';
// use this middleware for routes that do *not* require authentication
// but you want the user to automatically redirect somewhere
export const unauthed = createMiddleware<AuthCtx<null>>();
unauthed.use(isAuthenticated(authenticator, { successRedirect: '/dashboard' }));
unauthed.use(unauthed.routes());
// use this middleware for routes that *require* authentication
export const authed = createMiddleware<AuthCtx<User>>();
authed.use(isAuthenticated(authenticator, { failureRedirect: '/login' }));
authed.use(authed.routes());
// use this middleware if the route allows both authenticated and
// non-authenticated users
export const mdw = createMiddleware<AuthCtx<User | null>>();
mdw.use(isAuthenticated(authenticator));
mdw.use(async (ctx, next) => {
if (ctx.user) {
// ... do something with the user
} else {
// ... do something with a non-user
}
await next();
});
Now in your routes that require authentication
// in a route that requires auth
import { authed } from '~/middleware.server';
export const loader: LoaderFunction = (props) =>
authed.run(props, (ctx) => {
// no user can make it to this point without being authenticated
// and as a result we now have access to ctx.user which is `User`
// in this example
console.log(ctx.user); // { id: '123', email: 'cool@lib.bro' }
ctx.response = { text: `Hi ${ctx.user.email}!` };
});
Now in your routes that do not require authentication
// in a route that does *not* require auth
import { unauthed } from '~/middleware.server';
// `.run()` doesn't need any middleware, it'll run without it
export const loader = (props) => unauthed.run(props);