Skip to content

Commit ef89ff6

Browse files
committed
fix(services): exclude empty string fields in create and update operations
- Filter out object entries with empty string values before mutation calls - Apply filtering for both dashboards and widgets services on create and update methods - Ensure only meaningful data is sent to the backend avoiding empty strings - Maintain type assertions for filtered objects to keep type safety docs(migrations): add instructions for creating and applying custom SQL migrations - Document how to create empty migrations with custom SQL using prisma migrate dev --create-only - Provide examples of adding columns and indexes in custom migration files - Explain how to apply pending migrations in development and production environments - Detail steps to handle modified migrations before and after applying them - Clarify migration application is a local developer responsibility, not automated on Vercel feat(prisma): add unique partial indexes on Dashboard table with deletedAt filter - Drop existing indexes if they exist before creating new ones - Create unique indexes on userId and name, and on deviceId, excluding deleted entries - Use conditional index creation to avoid conflicts or errors if indexes already exist
1 parent af13830 commit ef89ff6

File tree

5 files changed

+154
-5
lines changed

5 files changed

+154
-5
lines changed

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,58 @@ The web application is deployed to Vercel, which is configured to automatically
115115

116116
Database migrations are generated based on changes to the Prisma schema and applied from the local developer's computer. Migrations must be applied locally by the developer and are not automatically applied by Vercel during deployment.
117117

118+
### Creating New Migrations with Custom SQL
119+
120+
To create a new empty migration (without changing the Prisma schema):
121+
122+
```bash
123+
cd web
124+
npx prisma migrate dev --name migration_name --create-only
125+
```
126+
127+
This creates an empty migration file in the `web/prisma/migrations` directory that you can manually edit with custom SQL commands.
128+
129+
Example of a custom migration file:
130+
```sql
131+
-- Add a new column to the User table
132+
ALTER TABLE "User" ADD COLUMN "lastLoginAt" TIMESTAMP(3);
133+
134+
-- Create an index on the new column
135+
CREATE INDEX "User_lastLoginAt_idx" ON "User"("lastLoginAt");
136+
```
137+
138+
### Applying Migrations
139+
140+
To apply pending migrations to your development database:
141+
142+
```bash
143+
cd web
144+
npx prisma migrate dev
145+
```
146+
147+
For production environments, use:
148+
149+
```bash
150+
cd web
151+
npx prisma migrate deploy
152+
```
153+
154+
### Working with Modified Migrations
155+
156+
If you've modified a migration but haven't committed it yet:
157+
158+
1. **If you haven't applied the migration yet:**
159+
- Simply edit the migration file as needed
160+
- Run `npx prisma migrate dev` to apply your changes
161+
162+
2. **If you've already applied the migration:**
163+
- Reset your database: `npx prisma migrate reset`
164+
- Edit the migration file as needed
165+
- Apply the migration: `npx prisma migrate dev`
166+
167+
3. **If you're working with a team and someone else has applied the original migration:**
168+
- Create a new migration with your changes: `npx prisma migrate dev --name fix_previous_migration`
169+
- This approach maintains consistency across team members
118170
## Deploying to Vercel
119171

120172
To deploy this project to Vercel:

web/README.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,60 @@ Database migrations are generated based on changes to the Prisma schema and appl
9797
2. Run `./node_modules/.bin/prisma migrate dev` to generate and apply migrations locally
9898
3. Commit the generated migration files to version control
9999

100-
Note: Migrations must be applied locally by the developer and are not automatically applied by Vercel during deployment.
100+
### Creating New Migrations with Custom SQL
101+
102+
To create a new empty migration (without changing the Prisma schema):
103+
104+
```bash
105+
npx prisma migrate dev --name add_new_feature --create-only
106+
```
107+
108+
This creates an empty migration file in the `prisma/migrations` directory that you can manually edit with custom SQL commands.
109+
110+
Example of a custom migration file (`prisma/migrations/20251208120000_add_new_feature/migration.sql`):
111+
```sql
112+
-- Add a new column to the User table
113+
ALTER TABLE "User" ADD COLUMN "lastLoginAt" TIMESTAMP(3);
101114

115+
-- Create an index on the new column
116+
CREATE INDEX "User_lastLoginAt_idx" ON "User"("lastLoginAt");
117+
118+
-- Add a default value for existing records
119+
UPDATE "User" SET "lastLoginAt" = NOW() WHERE "lastLoginAt" IS NULL;
120+
```
121+
122+
### Applying Migrations
123+
124+
To apply pending migrations to your development database:
125+
126+
```bash
127+
npx prisma migrate dev
128+
```
129+
130+
For production environments, use:
131+
132+
```bash
133+
npx prisma migrate deploy
134+
```
135+
136+
### Working with Modified Migrations
137+
138+
If you've modified a migration but haven't committed it yet:
139+
140+
1. **If you haven't applied the migration yet:**
141+
- Simply edit the migration file as needed
142+
- Run `npx prisma migrate dev` to apply your changes
143+
144+
2. **If you've already applied the migration:**
145+
- Reset your database: `npx prisma migrate reset`
146+
- Edit the migration file as needed
147+
- Apply the migration: `npx prisma migrate dev`
148+
149+
3. **If you're working with a team and someone else has applied the original migration:**
150+
- Create a new migration with your changes: `npx prisma migrate dev --name fix_previous_migration`
151+
- This approach maintains consistency across team members
152+
153+
Note: Migrations must be applied locally by the developer and are not automatically applied by Vercel during deployment.
102154
## Prisma Setup
103155

104156
This project uses Prisma as the ORM. After setting up your database:
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
DO $$
2+
BEGIN
3+
DROP INDEX IF EXISTS "UQ_DASHBOARD__USER_ID_NAME";
4+
EXCEPTION
5+
WHEN undefined_object THEN
6+
NULL;
7+
END
8+
$$;
9+
10+
-- CreateIndex
11+
CREATE UNIQUE INDEX IF NOT EXISTS "UQ_DASHBOARD__USER_ID_NAME" ON "Dashboard"("userId", "name")
12+
WHERE
13+
"deletedAt" IS NULL;
14+
15+
--
16+
DO $$
17+
BEGIN
18+
DROP INDEX IF EXISTS "UQ_DASHBOARD__DEVICE_ID";
19+
EXCEPTION
20+
WHEN undefined_object THEN
21+
NULL;
22+
END
23+
$$;
24+
25+
-- CreateIndex
26+
CREATE UNIQUE INDEX IF NOT EXISTS "UQ_DASHBOARD__DEVICE_ID" ON "Dashboard"("deviceId")
27+
WHERE
28+
"deletedAt" IS NULL;
29+

web/src/app/services/dashboards.service.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,23 @@ export class DashboardsService {
5252
private trpc = injectTrpcClient();
5353

5454
create(dashboard: CreateDashboardType) {
55-
return this.trpc.dashboards.create.mutate(dashboard);
55+
return this.trpc.dashboards.create.mutate(
56+
Object.fromEntries(
57+
Object.entries(dashboard).filter(([, value]) => value !== '')
58+
) as CreateDashboardType
59+
);
5660
}
5761

5862
read(id: string) {
5963
return this.trpc.dashboards.read.query({ id });
6064
}
6165

6266
update(dashboard: UpdateDashboardType) {
63-
return this.trpc.dashboards.update.mutate(dashboard);
67+
return this.trpc.dashboards.update.mutate(
68+
Object.fromEntries(
69+
Object.entries(dashboard).filter(([, value]) => value !== '')
70+
) as UpdateDashboardType
71+
);
6472
}
6573

6674
delete(id: string) {

web/src/app/services/widgets.service.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,23 @@ export class WidgetsService {
1414
private trpc = injectTrpcPureClient();
1515

1616
create(widget: CreateWidgetType) {
17-
return this.trpc.widgets.create.mutate(widget);
17+
return this.trpc.widgets.create.mutate(
18+
Object.fromEntries(
19+
Object.entries(widget).filter(([, value]) => value !== '')
20+
) as CreateWidgetType
21+
);
1822
}
1923

2024
read(id: string) {
2125
return this.trpc.widgets.read.query({ id });
2226
}
2327

2428
update(widget: UpdateWidgetType) {
25-
return this.trpc.widgets.update.mutate(widget);
29+
return this.trpc.widgets.update.mutate(
30+
Object.fromEntries(
31+
Object.entries(widget).filter(([, value]) => value !== '')
32+
) as UpdateWidgetType
33+
);
2634
}
2735

2836
delete(id: string) {

0 commit comments

Comments
 (0)