Skip to content

Commit 07f4782

Browse files
authored
Merge pull request #113 from JDIZM/apr-updates
Refactor handlers and add sentry
2 parents f01df7a + 0a5a377 commit 07f4782

35 files changed

+692
-499
lines changed

.env.example

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
NODE_ENV=dev
1+
NODE_ENV=development
22
APP_URL=http://localhost:4000
33
PORT=4000
4+
SENTRY_DSN=
45

56
###
67
## Database
@@ -17,3 +18,8 @@ POSTGRES_DB=postgres
1718
SUPABASE_URL=https://example.supabase.co
1819
SUPABASE_PK=example-key
1920
SUPABASE_AUTH_JWT_SECRET=abcdefghijklmnopqrstuvwxzyz1234567890
21+
22+
###
23+
# Feature Flags
24+
###
25+
FEATURE_FLAG_SENTRY_DEVELOPMENT=true

.eslintrc.cjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ module.exports = {
2222
settings: {
2323
// Tells eslint how to resolve imports
2424
"import/resolver": {
25-
// using the newer eslint-import-resolver-typescript plugin
2625
// see: https://www.npmjs.com/package/eslint-import-resolver-typescript
2726
node: {
2827
extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"]
@@ -37,9 +36,10 @@ module.exports = {
3736
}
3837
},
3938
rules: {
40-
// Add your own rules here to override ones from the extended configs.
4139
"@typescript-eslint/no-explicit-any": "warn",
42-
"arrow-parens": ["error", "always"]
40+
"arrow-parens": ["error", "always"],
41+
"@typescript-eslint/explicit-function-return-type": "warn",
42+
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }]
4343
},
4444
overrides: [
4545
{

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:20-alpine AS base
1+
FROM node:22-alpine AS base
22

33
ENV PNPM_VERSION=9.1.0
44

README.md

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# node-express-backend-component
1+
# supabase-express-api
22

33
- [tsx](https://github.com/esbuild-kit/tsx)
44
- [pkgroll](https://github.com/privatenumber/pkgroll)
@@ -16,11 +16,12 @@
1616
- [supabase-js](https://supabase.com/docs/reference/javascript/introduction)
1717
- [helmet](https://helmetjs.github.io/)
1818
- [cookie-parser](https://www.npmjs.com/package/cookie-parser)
19+
- [@sentry/node](https://docs.sentry.io/platforms/javascript/guides/node/)
1920

2021
A node/express backend API template for getting started with a new project that includes authentication, permissions, and a database configured to
2122
use [Supabase](https://supabase.io/) or a local/cloud Postgres database.
2223

23-
This comes pre-defined with a workspaces model that allows accounts (users) to create workspaces and invite other profiles (users presence within a workspace) to access the workspace (membership). see the [Permissions](#Permissions) section for more information on how permissions are defined.
24+
This comes pre-defined with a workspaces model that allows accounts (users) to create workspaces and invite other profiles (users presence within a workspace) to access the workspace (membership). see the [Permissions](#permissions) section for more information on how permissions are defined.
2425

2526
The contents of a workspace is not defined in this template and can be customized to suit the needs of the project.
2627

@@ -38,29 +39,51 @@ This project requires node.js to be installed. This project uses volta to manage
3839

3940
To install volta run the following command in the terminal.
4041

41-
```
42+
```bash
4243
curl https://get.volta.sh | bash
4344
```
4445

45-
You will need a Postgres database to run this project. You can use Docker to run a Postgres database or use a service like [Supabase](https://supabase.com/).
46+
You will need a Postgres database to run this project. You can use Docker to run a Postgres database or use a service like [Supabase](https://supabase.com/). The auth provider can be replaced with any other Auth providers eg Firebase, Auth0, Keycloak you just need to implement the authentication middleware to verify the token and decode the claims and modify the auth handlers to use your provider.
47+
48+
See the [Database](#database) section for more information on how to configure the database connection.
49+
50+
Authentication is handled by [Supabase](https://supabase.com/) and requires a Supabase account. You can sign up for a free account at [supabase.com](https://supabase.com/).
51+
52+
### Install PNPM
53+
54+
If using volta we can install corepack globally and let volta manage the binary, corepack will let us use the correct version of pnpm based on the `packageManager` field in the package.json file.
55+
56+
```bash
57+
npm install --global corepack@latest
58+
59+
corepack enable pnpm
60+
```
4661

47-
See the [Database](#Database) section for more information on how to configure the database connection.
62+
see the [installation docs for pnpm](https://pnpm.io/installation) and the [corepack docs](https://github.com/nodejs/corepack)
63+
for more information on how to install pnpm and corepack.
4864

4965
### ENV
5066

5167
Create a .env file in the root of the project and copy the contents of .env.example into it.
5268

53-
```
69+
```bash
5470
cp .env.example .env
5571
```
5672

5773
see the section on [Deployment with DigitalOcean](#deployment-with-digitalocean) for more information on how to configure the environment variables for deployment in different environments (eg. development and production).
5874

5975
### Install dependencies
6076

77+
```bash
78+
pnpm i
6179
```
62-
# install dependencies
63-
npm i
80+
81+
### Setup the database
82+
83+
The first time setting up the database you will need to run the `migrate` command to create the database tables and apply the migrations.
84+
85+
```bash
86+
pnpm run migrate
6487
```
6588

6689
## Testing
@@ -80,9 +103,10 @@ You can install the supabase cli for local development.
80103

81104
## Database
82105

83-
You can view the database with `npx drizzle-kit studio` or `npm run studio`.
106+
You can view the database with `pnpx drizzle-kit studio` or `pnpm run studio`.
84107

85108
You can spin up a local copy of the database and application with `docker-compose` but this is not required when using the Supabase db.
109+
86110
When using the supabase cli we can run a local copy of the db with `supabase start`.
87111

88112
### Developing locally with supabase
@@ -91,13 +115,13 @@ This will provide you with a connection string, you can update the local environ
91115

92116
`postgresql://postgres:postgres@localhost:54322/postgres`
93117

94-
Visit the Supabase dashboard: http://localhost:54323 and manage your database locally.
118+
Visit the Supabase dashboard: http://localhost:54323 and manage your database locally. Note: It appears that the database name needs to be `postgres` to be able to work with the Supabase dashboard.
95119

96120
### Local Postgres with Docker
97121

98122
You can spin up a local database and application with `docker-compose` but this is not required when using the Supabase db or cli.
99123

100-
```
124+
```bash
101125
docker compose up -d
102126
```
103127

@@ -125,38 +149,52 @@ Note: If you are using a local database and running the application within docke
125149

126150
### Migrations
127151

152+
When running the migrations for the first time on a new database run:
153+
154+
```bash
155+
pnpm run migrate
156+
```
157+
128158
When the schema/model is changed make sure to create a new migration and run it against the db.
129159

130-
1. Create a new migration
160+
### 1. Create a new migration
131161

132-
```
133-
npm run migrate:create
162+
```bash
163+
pnpm run migrate:create
134164

135165
```
136166

137-
2. Run the migrations
167+
### 2. Run the migrations
138168

139-
```
140-
npm run migrate:up
169+
```bash
170+
# first run the migrations
171+
pnpm run migrate:up
172+
173+
# then run
174+
pnpm migrate:push
141175
```
142176

143177
### Seeds
144178

145179
You can run the seeds to populate the database with initial data.
146180

147-
Before seeding the db make sure to run the migrations. If you want to populate the seeds with specific user email, password or id's related to the users created in Supabase. You can update the seeds in `./src/seeds/` with the required data and make sure to pass the `--supabase=true` flag to the seed command and it will create the users in Supabase and associate the id's with the db records.
181+
Before seeding the db make sure to run the migrations. If you want to populate the seeds with specific user email, password or id's related to the users created in Supabase. You can update the seeds in `./src/seeds/` with the required data.
148182

149-
Note: If you are creating users with Supabase you will need to confirm the email addresses.
183+
You will need to add these users to supabase auth and confirm the email addresses.
150184

151-
```
152-
npm run seed
185+
<!-- and make sure to pass the `--supabase=true` flag to the seed command and it will create the users in Supabase and associate the id's with the db records.
186+
187+
Note: If you are creating users with Supabase you will need to confirm the email addresses.-->
188+
189+
```bash
190+
pnpm run seed
153191
```
154192

155193
Be sure to update the seeds as new migrations are added.
156194

157195
## Build with docker
158196

159-
```
197+
```bash
160198
# build the app
161199
npm run build
162200

package.json

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"build:tsc": "rm -rf dist && tsc -p tsconfig.json",
2020
"test": "vitest --run --coverage",
2121
"migrate:create": "npx drizzle-kit generate",
22+
"migrate": "npx drizzle-kit migrate",
2223
"migrate:up": "npx drizzle-kit up",
2324
"migrate:push": "npx drizzle-kit push",
2425
"seed": "tsx ./src/services/db/seed.ts --supabase=$npm_config_supabase",
@@ -29,31 +30,33 @@
2930
"license": "ISC",
3031
"devDependencies": {
3132
"@types/jsonwebtoken": "^9.0.6",
32-
"@typescript-eslint/eslint-plugin": "^8.0.0",
33-
"@typescript-eslint/parser": "^8.0.0",
33+
"@typescript-eslint/eslint-plugin": "^8.29.0",
34+
"@typescript-eslint/parser": "^8.29.0",
3435
"@vitest/coverage-v8": "^2.0.0",
3536
"dotenv": "^16.4.5",
3637
"drizzle-kit": "^0.30.0",
3738
"eslint": "^8.57.0",
3839
"eslint-config-prettier": "^9.1.0",
3940
"eslint-import-resolver-node": "^0.3.9",
4041
"eslint-import-resolver-typescript": "^3.6.1",
41-
"eslint-plugin-import": "^2.29.1",
42+
"eslint-plugin-import": "^2.31.0",
4243
"pino-pretty": "^13.0.0",
4344
"pkgroll": "^2.1.1",
4445
"prettier": "^3.2.5",
45-
"tsx": "^4.11.0",
46-
"typescript": "^5.4.5",
46+
"tsx": "^4.19.3",
47+
"typescript": "^5.8.3",
4748
"vitest": "^2.0.0",
48-
"zod": "^3.23.8"
49+
"zod": "^3.24.2"
4950
},
5051
"files": [
5152
"dist"
5253
],
5354
"volta": {
54-
"node": "20.19.0"
55+
"node": "22.15.0"
5556
},
5657
"dependencies": {
58+
"@sentry/node": "^9.11.0",
59+
"@sentry/profiling-node": "^9.11.0",
5760
"@supabase/supabase-js": "^2.43.4",
5861
"@types/bcrypt": "^5.0.2",
5962
"@types/cookie-parser": "^1.4.7",
@@ -62,16 +65,15 @@
6265
"@types/node": "^20.12.13",
6366
"@types/pg": "^8.11.6",
6467
"bcrypt": "^5.1.1",
65-
"chalk": "^5.3.0",
6668
"cookie-parser": "^1.4.6",
6769
"cors": "^2.8.5",
6870
"drizzle-orm": "^0.41.0",
6971
"drizzle-zod": "^0.7.0",
7072
"express": "^4.19.2",
7173
"helmet": "^8.0.0",
7274
"jsonwebtoken": "^9.0.2",
73-
"pg": "^8.11.5",
74-
"pino": "^9.1.0",
75-
"pino-http": "^10.1.0"
75+
"pg": "^8.14.1",
76+
"pino": "^9.6.0",
77+
"pino-http": "^10.4.0"
7678
}
7779
}

src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const config = {
2929
db_port: Number(process.env.POSTGRES_PORT) || 5432,
3030
db_user: process.env.POSTGRES_USER || "postgres",
3131
db_password: process.env.POSTGRES_PASSWORD || "postgres",
32-
db_name: process.env.POSTGRES_DB || "test",
32+
db_name: process.env.POSTGRES_DB || "postgres",
3333
supabaseUrl: process.env.SUPABASE_URL || "https://example.supabase.co",
3434
supabaseKey: process.env.SUPABASE_PK || "example-key",
3535
jwtSecret: process.env.SUPABASE_AUTH_JWT_SECRET || "super-secret-key-that-should-be-replaced"

src/cors.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { logger } from "./helpers/index.ts";
2+
13
export const whitelist: RegExp[] = [
24
/^https?:\/\/localhost:3000$/,
35
/^https?:\/\/example\.com$/,
@@ -6,8 +8,10 @@ export const whitelist: RegExp[] = [
68
];
79

810
export const corsOptions = {
9-
origin: function (origin: string | undefined, callback: (a: null | Error, b?: boolean) => void) {
11+
origin: function (origin: string | undefined, callback: (a: null | Error, b?: boolean) => void): void {
12+
// Allows an undefined origin.
1013
const isOriginAllowed = origin ? whitelist.some((pattern) => pattern.test(origin)) : true;
14+
logger.debug("cors Origin:", origin);
1115

1216
if (isOriginAllowed) {
1317
callback(null, true);

src/features.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { Stage } from "./config.ts";
2+
import { STAGE } from "./config.ts";
3+
import { logger } from "./helpers/index.ts";
4+
5+
const FEATURE_FLAGS = {
6+
Example: "Example",
7+
Sentry: "Sentry"
8+
} as const;
9+
10+
export type FeatureFlag = keyof typeof FEATURE_FLAGS;
11+
export type FeatureFlagValue = (typeof FEATURE_FLAGS)[FeatureFlag];
12+
13+
const getFeatureFlag = (feature: FeatureFlag, stage: Stage): boolean => {
14+
const envVar = `FEATURE_FLAG_${feature.toUpperCase()}_${stage.toUpperCase()}`;
15+
return process.env[envVar] === "true";
16+
};
17+
18+
export const hasFeatureFlag = (feature: FeatureFlag, stage: Stage): boolean => {
19+
return getFeatureFlag(feature, stage);
20+
};
21+
22+
export const isSentryEnabled = hasFeatureFlag(FEATURE_FLAGS.Sentry, STAGE);
23+
logger.info(`FEATURE FLAG - ${FEATURE_FLAGS.Sentry} is enabled: ${isSentryEnabled}`);

0 commit comments

Comments
 (0)