inserting signature endpoint and database methods
This commit is contained in:
34
packages/backend/.gitignore
vendored
Normal file
34
packages/backend/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
11
packages/backend/drizzle.config.ts
Normal file
11
packages/backend/drizzle.config.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'dotenv/config';
|
||||
import { defineConfig } from 'drizzle-kit';
|
||||
|
||||
export default defineConfig({
|
||||
out: './drizzle',
|
||||
schema: './src/models/schema.ts',
|
||||
dialect: 'postgresql',
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL!,
|
||||
},
|
||||
});
|
||||
21
packages/backend/package.json
Normal file
21
packages/backend/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"@types/pg": "^8.15.6",
|
||||
"drizzle-kit": "^0.31.6",
|
||||
"tsx": "^4.20.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^17.2.3",
|
||||
"drizzle-orm": "^0.44.7",
|
||||
"pg": "^8.16.3",
|
||||
"zod": "^4.1.12"
|
||||
}
|
||||
}
|
||||
11
packages/backend/src/models/database.ts
Normal file
11
packages/backend/src/models/database.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'dotenv/config';
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||
|
||||
const DATABASE_URL = process.env.DATABASE_URL;
|
||||
if (!DATABASE_URL) {
|
||||
throw new Error("DATABASE_URL is missing")
|
||||
}
|
||||
|
||||
export const db = drizzle(DATABASE_URL);
|
||||
|
||||
8
packages/backend/src/models/index.ts
Normal file
8
packages/backend/src/models/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { db } from "./database";
|
||||
import { signaturesTable } from "./schema";
|
||||
|
||||
export const insertSignature = async (signature: typeof signaturesTable.$inferInsert): Promise<typeof signaturesTable.$inferSelect | undefined> => {
|
||||
const [insertedSignature] = await db.insert(signaturesTable).values(signature).returning();
|
||||
|
||||
return insertedSignature;
|
||||
}
|
||||
8
packages/backend/src/models/schema.ts
Normal file
8
packages/backend/src/models/schema.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { pgTable, text, uuid } from "drizzle-orm/pg-core";
|
||||
|
||||
export const signaturesTable = pgTable("signatures", {
|
||||
id: uuid().primaryKey().defaultRandom(),
|
||||
email: text().notNull(),
|
||||
name: text(),
|
||||
comment: text(),
|
||||
});
|
||||
29
packages/backend/src/routes/sign-petition.ts
Normal file
29
packages/backend/src/routes/sign-petition.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { z } from "zod"
|
||||
import { insertSignature } from "../models";
|
||||
|
||||
const signPetitionSchema = z.object({
|
||||
email: z.string(),
|
||||
name: z.string().trim().min(1).max(30).optional(),
|
||||
comment: z.string().trim().min(10).max(10_000).optional(),
|
||||
})
|
||||
|
||||
export const signPetition = async (req: Request): Promise<Response> => {
|
||||
const body = await req.json()
|
||||
|
||||
const validatedBody = signPetitionSchema.safeParse(body);
|
||||
if (!validatedBody.success) {
|
||||
return Response.json({ error: validatedBody.error }, { status: 400 });
|
||||
}
|
||||
|
||||
const insertedSignature = await insertSignature({
|
||||
email: validatedBody.data.email,
|
||||
name: validatedBody.data.name,
|
||||
comment: validatedBody.data.comment,
|
||||
})
|
||||
|
||||
if (!insertedSignature) {
|
||||
return Response.json({ error: "inserting signature in database" }, { status: 500 });
|
||||
}
|
||||
|
||||
return Response.json(insertedSignature, { status: 200 });
|
||||
}
|
||||
29
packages/backend/tsconfig.json
Normal file
29
packages/backend/tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user