Skip to content

Commit

Permalink
Add express recipe for @edgedb/create (#826)
Browse files Browse the repository at this point in the history
  • Loading branch information
scotttrinh authored Jan 17, 2024
1 parent 6fd983f commit 95cde2b
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/create/src/recipes/_base/template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"edgedb": "1.x"
},
"devDependencies": {
"@edgedb/generate": "^0.4.1",
"@edgedb/generate": "0.x",
"@typescript-eslint/eslint-plugin": "6.x",
"@typescript-eslint/parser": "6.x",
"@eslint/eslintrc": "2.x",
Expand Down
35 changes: 35 additions & 0 deletions packages/create/src/recipes/express/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import path from "node:path";
import debug from "debug";
import { updatePackage } from "write-package";

import type { BaseOptions, Recipe } from "../types.js";
import { copyTemplateFiles } from "../../utils.js";

const logger = debug("@edgedb/create:recipe:express");

Expand All @@ -9,6 +13,37 @@ const recipe: Recipe = {
},
async apply(baseOptions: BaseOptions) {
logger("Running express recipe");

const dirname = path.dirname(new URL(import.meta.url).pathname);
await copyTemplateFiles(
path.resolve(dirname, "./template"),
baseOptions.projectDir
);

await updatePackage(baseOptions.projectDir, {
sideEffects: false,
type: "module",
scripts: {
dev: "DEBUG=* EDGEDB_CLIENT_SECURITY=insecure_dev_mode tsx watch --clear-screen=false src/index.ts",
build: "tsc",
lint: "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
typecheck: "tsc --noEmit",
},
dependencies: {
...(baseOptions.useEdgeDBAuth
? { "@edgedb/auth-express": "^0.1.0-beta.2" }
: {}),
"cookie-parser": "^1.4.6",
express: "^4.18.2",
},
devDependencies: {
"@types/cookie-parser": "^1.4.6",
"@types/express": "^4.17.21",
"@types/node": "^20.10.3",
tsx: "^4.6.2",
typescript: "^5.3.2",
},
});
},
};

Expand Down
51 changes: 51 additions & 0 deletions packages/create/src/recipes/express/template/src/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { type NextFunction, type Response, Router } from "express";
import createExpressAuth, {
type AuthRequest,
type CallbackRequest,
} from "@edgedb/auth-express";
import { client } from "./db.js";
import { PORT } from "./env.js";

export const auth = createExpressAuth(client, {
baseUrl: `http://localhost:${PORT}`,
});

export const requireAuth = async (
req: AuthRequest,
res: Response,
next: NextFunction
) => {
if (!(await req.session?.isSignedIn())) {
res.redirect("/signin");
} else {
next();
}
};

/************
* Sign Out *
************/

export const signoutRoute = Router().get(
"/",
auth.signout,
async (_: AuthRequest, res: Response) => {
res.redirect("/");
}
);

/***************
* Built-in UI *
***************/

const builtinCallback = async (req: CallbackRequest, res: Response) => {
if (req.isSignUp) {
return res.redirect("/onboarding");
}

res.redirect("/");
};

export const builtinUIRouter = auth.createBuiltinRouter({
callback: [builtinCallback],
});
3 changes: 3 additions & 0 deletions packages/create/src/recipes/express/template/src/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createClient } from "edgedb";

export const client = createClient();
1 change: 1 addition & 0 deletions packages/create/src/recipes/express/template/src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const PORT = process.env.PORT || 3333;
76 changes: 76 additions & 0 deletions packages/create/src/recipes/express/template/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import express from "express";
import cookieParser from "cookie-parser";
import { type AuthRequest } from "@edgedb/auth-express";

import { styles } from "./styles.js";
import { auth, requireAuth, signoutRoute, builtinUIRouter } from "./auth.js";
import { PORT } from "./env.js";

const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(auth.createSessionMiddleware());

router.get("/api/deep-thought", requireAuth, async (req: AuthRequest, res) => {
// See more examples of making queries here: https://github.com/edgedb/edgedb-examples/blob/main/express-auth/todos.ts
const answer = await req.session!.client.query<number>("select 42;");
res.json(answer);
});

app.use("/auth", builtinUIRouter);
app.use("/auth/signout", signoutRoute);

const pageTemplate = (body: string) => `
<html>
<head>
<style>
${styles}
</style>
</head>
<body>
<main>
${body}
</main>
</body>
</html>
`;

app.get("/onboarding", requireAuth, (req, res) => {
res.send(
pageTemplate(`
<h1>Onboarding</h1>
<a href="/auth/signout">Sign out</a>
`)
);
});

app.get("/dashboard", requireAuth, (req, res) => {
res.send(
pageTemplate(`
<h1>Dashboard</h1>
<a href="/auth/signout">Sign out</a>
`)
);
});

app.get("/verify", (req, res) => {
res.send(
pageTemplate(`
<p>Check your email for a verification message.</p>
`)
);
});

app.get("/signin", (_, res) => {
res.redirect("/auth/signin");
});

app.get("/", requireAuth, async (req: AuthRequest, res) => {
res.redirect("/dashboard");
});

app.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`);
});
68 changes: 68 additions & 0 deletions packages/create/src/recipes/express/template/src/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
export const styles = `
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
main {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
h1 {
color: #333;
}
a {
color: #0066cc;
}
.errors {
margin-bottom: 20px;
display: flex;
flex-direction: column;
gap: 5px;
}
.errors:empty {
display: none;
}
p.error {
display: block;
padding: 10px;
background-color: #ffcccc;
color: #cc0000;
}
.form-control {
margin-bottom: 10px;
width: 100%;
}
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 10px;
margin-bottom: 10px;
}
.actions {
display: flex;
align-items: center;
gap: 5px;
}
button[type="submit"] {
padding: 10px 20px;
background-color: #0066cc;
color: #fff;
border: none;
}
`;

0 comments on commit 95cde2b

Please sign in to comment.