Skip to content

Commit 17fbcfb

Browse files
committed
Use loader context pattern for injecting DB, authenticator
1 parent 5a45f76 commit 17fbcfb

File tree

12 files changed

+94
-25
lines changed

12 files changed

+94
-25
lines changed

.dev.vars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SESSION_SECRET=secret

.env.prod renamed to .env

File renamed without changes.

.env.local

Lines changed: 0 additions & 2 deletions
This file was deleted.

app/load-context.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { getDB } from './services/db/utilities';
2+
import {
3+
AppLoadContext,
4+
createCookieSessionStorage,
5+
} from '@remix-run/cloudflare';
6+
import { Authenticator } from 'remix-auth';
7+
import { FormStrategy } from 'remix-auth-form';
8+
import { login } from './services/auth/session';
9+
import { Cloudflare } from './types/cloudflare';
10+
11+
export interface WorkerEnv extends Env {
12+
SESSION_SECRET: string;
13+
}
14+
15+
declare module '@remix-run/cloudflare' {
16+
interface AppLoadContext {
17+
cloudflare: Cloudflare;
18+
db: ReturnType<typeof getDB>;
19+
authenticator: Authenticator;
20+
}
21+
}
22+
23+
type GetLoadContext = (args: {
24+
request: Request;
25+
context: { cloudflare: Cloudflare }; // load context _before_ augmentation
26+
}) => AppLoadContext;
27+
28+
export const getLoadContext: GetLoadContext = ({ context }) => {
29+
const db = getDB(context);
30+
31+
const sessionStorage = createCookieSessionStorage({
32+
cookie: {
33+
name: '_session',
34+
sameSite: 'lax',
35+
path: '/',
36+
httpOnly: true,
37+
secrets: [context.cloudflare.env.SESSION_SECRET],
38+
secure: true,
39+
},
40+
});
41+
42+
const authenticator = new Authenticator(sessionStorage).use(
43+
new FormStrategy(async ({ form, context }) => {
44+
if (context) {
45+
const username = form.get('username')?.toString();
46+
const password = form.get('password')?.toString();
47+
48+
if (username && password) {
49+
const user = await login(username, password, db);
50+
51+
return user;
52+
}
53+
}
54+
}),
55+
'user-pass',
56+
);
57+
58+
return {
59+
...context,
60+
db,
61+
authenticator,
62+
};
63+
};

app/root.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import { type LoaderFunctionArgs, json } from '@remix-run/cloudflare';
22
import { Links, Meta, Outlet, Scripts, useLoaderData } from '@remix-run/react';
33
import { books } from 'db/schema';
4-
import { drizzle } from 'drizzle-orm/d1';
54

65
export const loader = async ({ context }: LoaderFunctionArgs) => {
7-
const db = drizzle(context.cloudflare.env.DB);
8-
9-
const allBooks = await db.select().from(books).all();
6+
const allBooks = await context.db.select().from(books).all();
107

118
return json({ books: allBooks });
129
};

app/services/db/utilities.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { AppLoadContext } from '@remix-run/cloudflare';
21
import { drizzle } from 'drizzle-orm/d1';
32
import * as schema from 'db/schema';
3+
import { Cloudflare } from '~/types/cloudflare';
44

5-
export const getDB = (context: AppLoadContext) =>
5+
export const getDB = (context: { cloudflare: Cloudflare }) =>
66
drizzle(context.cloudflare.env.DB, { schema });
77

88
export type AppDB = ReturnType<typeof getDB>;

app/types/cloudflare.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { PlatformProxy } from 'wrangler';
2+
3+
export interface WorkerEnv extends Env {
4+
SESSION_SECRET: string;
5+
}
6+
7+
export type Cloudflare = Omit<PlatformProxy<Env>, 'dispose'>;

app/types/load-context.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

functions/[[path]].ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,9 @@ import { createPagesFunctionHandler } from '@remix-run/cloudflare-pages';
44
// @ts-ignore - the server build file is generated by `remix vite:build`
55
// eslint-disable-next-line import/no-unresolved
66
import * as build from '../build/server';
7+
import { getLoadContext } from '~/load-context';
78

8-
export const onRequest = createPagesFunctionHandler({ build });
9+
export const onRequest = createPagesFunctionHandler({
10+
build,
11+
getLoadContext,
12+
});

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@
66
"type": "module",
77
"main": "build/client/index.js",
88
"scripts": {
9-
"dev": "dotenvx run -f .env.local -- remix vite:dev --open",
10-
"build": "dotenvx run -f .env.prod -- remix vite:build",
9+
"dev": "remix vite:dev --open",
10+
"build": "remix vite:build",
1111
"lint": "eslint . --cache",
1212
"format:check": "prettier . --check",
1313
"format:write": "prettier . --write",
1414
"cf-typegen": "wrangler types",
1515
"db:update": "drizzle-kit up",
1616
"db:migrate:create": "drizzle-kit generate",
17-
"db:migrate:apply:local": "dotenvx run -f .env.local -- sh scripts/db-migrate.sh",
18-
"db:migrate:apply:prod": "dotenvx run -f .env.prod -- sh scripts/db-migrate-prod.sh",
17+
"db:migrate:apply:local": "dotenvx run -- sh scripts/db-migrate.sh",
18+
"db:migrate:apply:prod": "dotenvx run -- sh scripts/db-migrate-prod.sh",
1919
"db:studio:local": "LOCAL_DB_PATH=$(find .wrangler/state/v3/d1/miniflare-D1DatabaseObject -type f -name '*.sqlite' -print -quit) drizzle-kit studio",
20-
"db:sync": "dotenvx run -f .env.local -- sh scripts/db-sync.sh"
20+
"db:sync": "dotenvx run -- sh scripts/db-sync.sh"
2121
},
2222
"keywords": [],
2323
"author": "",

vite.config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import {
44
} from '@remix-run/dev';
55
import { defineConfig } from 'vite';
66
import tsconfigPaths from 'vite-tsconfig-paths';
7+
import { getLoadContext } from '~/load-context';
78

89
export default defineConfig({
9-
plugins: [remixCloudflareDevProxy(), remix(), tsconfigPaths()],
10+
plugins: [
11+
remixCloudflareDevProxy({
12+
getLoadContext,
13+
}),
14+
remix(),
15+
tsconfigPaths(),
16+
],
1017
});

worker-configuration.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
// Generated by Wrangler on Sun May 12 2024 17:03:53 GMT-0400 (Eastern Daylight Time)
1+
// Generated by Wrangler on Sun May 12 2024 22:57:14 GMT-0400 (Eastern Daylight Time)
22
// by running `wrangler types`
33

44
interface Env {
55
CLOUDFLARE_ACCOUNT_ID: '887644ede3080f7152aa22e9ecf52f0f';
6+
SESSION_SECRET: string;
67
DB: D1Database;
78
}

0 commit comments

Comments
 (0)