render-create 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +207 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.js +45 -0
  4. package/dist/commands/check.d.ts +8 -0
  5. package/dist/commands/check.js +96 -0
  6. package/dist/commands/init.d.ts +12 -0
  7. package/dist/commands/init.js +1201 -0
  8. package/dist/commands/sync.d.ts +8 -0
  9. package/dist/commands/sync.js +126 -0
  10. package/dist/types.d.ts +246 -0
  11. package/dist/types.js +4 -0
  12. package/dist/utils.d.ts +53 -0
  13. package/dist/utils.js +142 -0
  14. package/package.json +65 -0
  15. package/templates/LINTING_SETUP.md +205 -0
  16. package/templates/README_TEMPLATE.md +68 -0
  17. package/templates/STYLE_GUIDE.md +241 -0
  18. package/templates/assets/favicon.png +0 -0
  19. package/templates/assets/favicon.svg +17 -0
  20. package/templates/biome.json +43 -0
  21. package/templates/cursor/rules/drizzle.mdc +165 -0
  22. package/templates/cursor/rules/fastify.mdc +132 -0
  23. package/templates/cursor/rules/general.mdc +112 -0
  24. package/templates/cursor/rules/nextjs.mdc +89 -0
  25. package/templates/cursor/rules/python.mdc +89 -0
  26. package/templates/cursor/rules/react.mdc +200 -0
  27. package/templates/cursor/rules/sqlalchemy.mdc +205 -0
  28. package/templates/cursor/rules/tailwind.mdc +139 -0
  29. package/templates/cursor/rules/typescript.mdc +112 -0
  30. package/templates/cursor/rules/vite.mdc +169 -0
  31. package/templates/cursor/rules/workflows.mdc +349 -0
  32. package/templates/docker-compose.example.yml +55 -0
  33. package/templates/drizzle/db-index.ts +15 -0
  34. package/templates/drizzle/drizzle.config.ts +10 -0
  35. package/templates/drizzle/schema.ts +12 -0
  36. package/templates/env.example +15 -0
  37. package/templates/fastapi/app/__init__.py +1 -0
  38. package/templates/fastapi/app/config.py +12 -0
  39. package/templates/fastapi/app/database.py +16 -0
  40. package/templates/fastapi/app/models.py +13 -0
  41. package/templates/fastapi/main.py +22 -0
  42. package/templates/fastify/index.ts +40 -0
  43. package/templates/github/CODEOWNERS +10 -0
  44. package/templates/github/ISSUE_TEMPLATE/bug_report.md +39 -0
  45. package/templates/github/ISSUE_TEMPLATE/feature_request.md +23 -0
  46. package/templates/github/PULL_REQUEST_TEMPLATE.md +25 -0
  47. package/templates/gitignore/node.gitignore +41 -0
  48. package/templates/gitignore/python.gitignore +49 -0
  49. package/templates/multi-api/README.md +60 -0
  50. package/templates/multi-api/gitignore +28 -0
  51. package/templates/multi-api/node-api/drizzle.config.ts +10 -0
  52. package/templates/multi-api/node-api/package-simple.json +13 -0
  53. package/templates/multi-api/node-api/package.json +16 -0
  54. package/templates/multi-api/node-api/src/db/index.ts +13 -0
  55. package/templates/multi-api/node-api/src/db/schema.ts +9 -0
  56. package/templates/multi-api/node-api/src/index-simple.ts +36 -0
  57. package/templates/multi-api/node-api/src/index.ts +50 -0
  58. package/templates/multi-api/node-api/tsconfig.json +20 -0
  59. package/templates/multi-api/python-api/app/__init__.py +1 -0
  60. package/templates/multi-api/python-api/app/config.py +12 -0
  61. package/templates/multi-api/python-api/app/database.py +16 -0
  62. package/templates/multi-api/python-api/app/models.py +13 -0
  63. package/templates/multi-api/python-api/main-simple.py +25 -0
  64. package/templates/multi-api/python-api/main.py +44 -0
  65. package/templates/multi-api/python-api/requirements-simple.txt +3 -0
  66. package/templates/multi-api/python-api/requirements.txt +8 -0
  67. package/templates/next/globals.css +126 -0
  68. package/templates/next/layout.tsx +34 -0
  69. package/templates/next/next.config.static.ts +10 -0
  70. package/templates/next/page-fullstack.tsx +120 -0
  71. package/templates/next/page.tsx +72 -0
  72. package/templates/presets.json +581 -0
  73. package/templates/ruff.toml +30 -0
  74. package/templates/tsconfig.base.json +17 -0
  75. package/templates/vite/index.css +127 -0
  76. package/templates/vite/vite.config.ts +7 -0
  77. package/templates/worker/py/cron.py +53 -0
  78. package/templates/worker/py/worker.py +95 -0
  79. package/templates/worker/py/workflow.py +73 -0
  80. package/templates/worker/ts/cron.ts +49 -0
  81. package/templates/worker/ts/worker.ts +84 -0
  82. package/templates/worker/ts/workflow.ts +67 -0
@@ -0,0 +1,169 @@
1
+ ---
2
+ description: Vite + React SPA conventions
3
+ globs: ["**/vite.config.*", "**/src/**/*.tsx", "**/src/**/*.ts"]
4
+ ---
5
+
6
+ # Vite + React SPA Conventions
7
+
8
+ ## When to Use Vite vs Next.js
9
+
10
+ **Use Vite when:**
11
+ - Building a pure SPA (no SSR needed)
12
+ - API is separate (backend in different service)
13
+ - Simple static site or dashboard
14
+ - Maximum build speed
15
+
16
+ **Use Next.js when:**
17
+ - Need SSR or static generation
18
+ - SEO is important
19
+ - Full-stack with API routes
20
+ - Complex routing needs
21
+
22
+ ## Project Structure
23
+
24
+ ```
25
+ src/
26
+ ├── main.tsx # Entry point
27
+ ├── App.tsx # Root component
28
+ ├── index.css # Global styles (Tailwind)
29
+ ├── components/ # Reusable components
30
+ │ ├── ui/ # Base UI components
31
+ │ └── features/ # Feature-specific components
32
+ ├── hooks/ # Custom hooks
33
+ ├── lib/ # Utilities
34
+ │ └── utils.ts # cn() and helpers
35
+ ├── types/ # TypeScript types
36
+ └── assets/ # Static assets
37
+ ```
38
+
39
+ ## Vite Configuration
40
+
41
+ ```typescript
42
+ // vite.config.ts
43
+ import { defineConfig } from "vite";
44
+ import react from "@vitejs/plugin-react";
45
+ import path from "path";
46
+
47
+ export default defineConfig({
48
+ plugins: [react()],
49
+ resolve: {
50
+ alias: {
51
+ "@": path.resolve(__dirname, "./src"),
52
+ },
53
+ },
54
+ server: {
55
+ port: 3000,
56
+ proxy: {
57
+ "/api": {
58
+ target: "http://localhost:8080",
59
+ changeOrigin: true,
60
+ },
61
+ },
62
+ },
63
+ });
64
+ ```
65
+
66
+ ## Path Aliases
67
+
68
+ ```json
69
+ // tsconfig.json
70
+ {
71
+ "compilerOptions": {
72
+ "baseUrl": ".",
73
+ "paths": {
74
+ "@/*": ["./src/*"]
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ Usage:
81
+ ```typescript
82
+ import { Button } from "@/components/ui/Button";
83
+ import { useAuth } from "@/hooks/useAuth";
84
+ ```
85
+
86
+ ## Environment Variables
87
+
88
+ ```bash
89
+ # .env
90
+ VITE_API_URL=http://localhost:8080
91
+ VITE_APP_NAME=My App
92
+ ```
93
+
94
+ ```typescript
95
+ // Access in code - must be prefixed with VITE_
96
+ const apiUrl = import.meta.env.VITE_API_URL;
97
+
98
+ // Type definitions
99
+ // src/vite-env.d.ts
100
+ /// <reference types="vite/client" />
101
+
102
+ interface ImportMetaEnv {
103
+ readonly VITE_API_URL: string;
104
+ readonly VITE_APP_NAME: string;
105
+ }
106
+
107
+ interface ImportMeta {
108
+ readonly env: ImportMetaEnv;
109
+ }
110
+ ```
111
+
112
+ ## Entry Point
113
+
114
+ ```tsx
115
+ // main.tsx
116
+ import React from "react";
117
+ import ReactDOM from "react-dom/client";
118
+ import App from "./App";
119
+ import "./index.css";
120
+
121
+ ReactDOM.createRoot(document.getElementById("root")!).render(
122
+ <React.StrictMode>
123
+ <App />
124
+ </React.StrictMode>
125
+ );
126
+ ```
127
+
128
+ ## API Calls
129
+
130
+ ```typescript
131
+ // lib/api.ts
132
+ const API_URL = import.meta.env.VITE_API_URL;
133
+
134
+ export async function fetchData<T>(endpoint: string): Promise<T> {
135
+ const response = await fetch(`${API_URL}${endpoint}`);
136
+ if (!response.ok) {
137
+ throw new Error(`HTTP ${response.status}`);
138
+ }
139
+ return response.json();
140
+ }
141
+ ```
142
+
143
+ ## Scripts
144
+
145
+ ```json
146
+ // package.json
147
+ {
148
+ "scripts": {
149
+ "dev": "vite",
150
+ "build": "tsc && vite build",
151
+ "preview": "vite preview",
152
+ "lint": "biome check --write src/"
153
+ }
154
+ }
155
+ ```
156
+
157
+ ## Build Output
158
+
159
+ ```bash
160
+ # Build for production
161
+ npm run build
162
+
163
+ # Output in dist/
164
+ dist/
165
+ ├── index.html
166
+ ├── assets/
167
+ │ ├── index-[hash].js
168
+ │ └── index-[hash].css
169
+ ```
@@ -0,0 +1,349 @@
1
+ ---
2
+ description: Render Workflows conventions for distributed task execution
3
+ globs: ["**/workflow/**/*.ts", "**/workflow/**/*.py", "**/workflows/**/*.ts", "**/workflows/**/*.py", "**/worker/**/*.ts", "**/worker/**/*.py"]
4
+ ---
5
+
6
+ # Render Workflows Conventions
7
+
8
+ Render Workflows provide managed execution of distributed tasks with rapid spin-up and automatic retries. Use workflows for ETL pipelines, AI agents, or any job that benefits from distributed background execution.
9
+
10
+ ## SDK Installation
11
+
12
+ TypeScript:
13
+ ```bash
14
+ npm install @renderinc/sdk
15
+ ```
16
+
17
+ Python:
18
+ ```bash
19
+ pip install render_sdk
20
+ ```
21
+
22
+ ## Project Structure
23
+
24
+ TypeScript:
25
+ ```
26
+ workflow/
27
+ ├── src/
28
+ │ ├── tasks/
29
+ │ │ ├── index.ts # Task exports
30
+ │ │ ├── math.ts # Domain-specific tasks
31
+ │ │ └── data.ts # Data processing tasks
32
+ │ └── main.ts # Entry point
33
+ ├── package.json
34
+ └── tsconfig.json
35
+ ```
36
+
37
+ Python:
38
+ ```
39
+ workflow/
40
+ ├── tasks/
41
+ │ ├── __init__.py
42
+ │ ├── math.py # Domain-specific tasks
43
+ │ └── data.py # Data processing tasks
44
+ ├── main.py # Entry point
45
+ └── requirements.txt
46
+ ```
47
+
48
+ ## Defining Tasks
49
+
50
+ ### TypeScript
51
+
52
+ ```typescript
53
+ import "dotenv/config";
54
+ import { task, type Retry } from "@renderinc/sdk/workflows";
55
+
56
+ // Retry configuration
57
+ const retry: Retry = {
58
+ maxRetries: 3,
59
+ waitDurationMs: 1000,
60
+ factor: 1.5,
61
+ };
62
+
63
+ // Subtask: assign to const (called by other tasks)
64
+ const square = task({ name: "square" }, function square(a: number): number {
65
+ console.log(`Calculating square of ${a}`);
66
+ return a * a;
67
+ });
68
+
69
+ // Root task: don't assign to variable
70
+ task(
71
+ {
72
+ name: "processData",
73
+ timeoutSeconds: 300,
74
+ retry,
75
+ },
76
+ async function processData(input: string): Promise<string> {
77
+ return input.toUpperCase();
78
+ }
79
+ );
80
+
81
+ // Root task that calls subtasks
82
+ task(
83
+ { name: "addSquares", timeoutSeconds: 300, retry },
84
+ async function addSquares(a: number, b: number): Promise<number> {
85
+ const result1 = await square(a);
86
+ const result2 = await square(b);
87
+ return result1 + result2;
88
+ }
89
+ );
90
+ ```
91
+
92
+ ### Python
93
+
94
+ ```python
95
+ import asyncio
96
+ import logging
97
+
98
+ from dotenv import load_dotenv
99
+ from render_sdk import Retry, Workflows
100
+
101
+ load_dotenv()
102
+
103
+ logger = logging.getLogger(__name__)
104
+
105
+ # Retry configuration
106
+ retry = Retry(max_retries=3, wait_duration_ms=1000, backoff_scaling=1.5)
107
+
108
+ # Initialize Workflows app
109
+ app = Workflows(
110
+ default_retry=retry,
111
+ default_timeout=300,
112
+ auto_start=True,
113
+ )
114
+
115
+ # Subtask (called by other tasks)
116
+ @app.task
117
+ def square(a: int) -> int:
118
+ """Square a number."""
119
+ return a * a
120
+
121
+ # Root task that calls subtasks
122
+ @app.task
123
+ async def add_squares(a: int, b: int) -> int:
124
+ """Add the squares of two numbers."""
125
+ result1 = await square(a)
126
+ result2 = await square(b)
127
+ return result1 + result2
128
+
129
+ @app.task
130
+ async def process_data(data: str) -> str:
131
+ """Process data."""
132
+ return data.upper()
133
+
134
+ # Fan-out pattern
135
+ @app.task
136
+ async def fan_out(items: list[str]) -> list[str]:
137
+ """Process items in parallel."""
138
+ tasks = [process_data(item) for item in items]
139
+ results = await asyncio.gather(*tasks)
140
+ return list(results)
141
+ ```
142
+
143
+ ## Running Tasks (Client)
144
+
145
+ ### TypeScript
146
+
147
+ ```typescript
148
+ import { Render, ClientError, ServerError, AbortError } from "@renderinc/sdk";
149
+
150
+ const render = new Render(); // Uses RENDER_API_KEY env var
151
+
152
+ async function runWorkflow() {
153
+ try {
154
+ // Run task and wait for completion
155
+ const result = await render.workflows.runTask(
156
+ "my-workflow/process-data",
157
+ ["input-value"]
158
+ );
159
+
160
+ console.log("Status:", result.status);
161
+ console.log("Results:", result.results);
162
+
163
+ // List recent task runs
164
+ const runs = await render.workflows.listTaskRuns({ limit: 10 });
165
+
166
+ // Get specific task run details
167
+ const details = await render.workflows.getTaskRun(result.id);
168
+
169
+ } catch (error) {
170
+ if (error instanceof ClientError) {
171
+ console.error("Client error:", error.statusCode);
172
+ } else if (error instanceof ServerError) {
173
+ console.error("Server error:", error.statusCode);
174
+ } else if (error instanceof AbortError) {
175
+ console.error("Request aborted");
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ ### Python
182
+
183
+ ```python
184
+ import asyncio
185
+ from render_sdk import Render
186
+ from render_sdk.client import ListTaskRunsParams
187
+ from render_sdk.client.errors import RenderError, TaskRunError
188
+
189
+ async def run_workflow():
190
+ render = Render() # Uses RENDER_API_KEY env var
191
+
192
+ try:
193
+ # Run task and get awaitable result
194
+ task_run = await render.workflows.run_task(
195
+ "my-workflow/process-data",
196
+ {"arg1": "value"}
197
+ )
198
+ print(f"Task started: {task_run.id}")
199
+
200
+ # Wait for completion
201
+ result = await task_run
202
+ print(f"Status: {result.status}")
203
+ print(f"Results: {result.results}")
204
+
205
+ # List recent task runs
206
+ params = ListTaskRunsParams(limit=10)
207
+ runs = await render.workflows.list_task_runs(params)
208
+
209
+ except TaskRunError as e:
210
+ print(f"Task failed: {e}")
211
+ except RenderError as e:
212
+ print(f"API error: {e}")
213
+
214
+ asyncio.run(run_workflow())
215
+ ```
216
+
217
+ ## Best Practices
218
+
219
+ ### Task Design
220
+
221
+ 1. **Keep tasks focused**: Each task should do one thing well
222
+ 2. **Use subtasks for parallelism**: Fan out work across multiple instances
223
+ 3. **Make tasks idempotent**: Tasks may retry, so handle duplicate execution
224
+ 4. **Return JSON-serializable data**: All inputs and outputs must be JSON-compatible
225
+
226
+ ### Retry Configuration
227
+
228
+ Import and type your retry config:
229
+
230
+ ```typescript
231
+ import { task, type Retry } from "@renderinc/sdk/workflows";
232
+
233
+ // Retry configuration
234
+ const retry: Retry = {
235
+ maxRetries: 3,
236
+ waitDurationMs: 1000,
237
+ factor: 1.5, // Exponential backoff: 1s, 1.5s, 2.25s
238
+ };
239
+
240
+ task(
241
+ { name: "fetchData", timeoutSeconds: 300, retry },
242
+ async function fetchData(url: string) {
243
+ const response = await fetch(url);
244
+ return response.json();
245
+ }
246
+ );
247
+
248
+ // For non-idempotent operations, disable retries
249
+ task(
250
+ { name: "sendEmail", retry: { maxRetries: 0, waitDurationMs: 0 } },
251
+ async function sendEmail(to: string, subject: string) {
252
+ // Don't retry to avoid duplicates
253
+ }
254
+ );
255
+ ```
256
+
257
+ ### Parallelizing Subtasks
258
+
259
+ TypeScript:
260
+ ```typescript
261
+ task(
262
+ { name: "processImages" },
263
+ async function processImages(urls: string[]): Promise<string[]> {
264
+ // Run all subtasks in parallel
265
+ const results = await Promise.all(
266
+ urls.map(url => processImage(url))
267
+ );
268
+ return results;
269
+ }
270
+ );
271
+ ```
272
+
273
+ Python:
274
+ ```python
275
+ @app.task
276
+ async def process_images(urls: list[str]) -> list[str]:
277
+ """Process multiple images in parallel."""
278
+ tasks = [process_image(url) for url in urls]
279
+ results = await asyncio.gather(*tasks)
280
+ return list(results)
281
+ ```
282
+
283
+ ### Error Handling
284
+
285
+ ```typescript
286
+ task(
287
+ { name: "robustTask" },
288
+ async function robustTask(data: unknown): Promise<Result> {
289
+ try {
290
+ // Main logic
291
+ return await processData(data);
292
+ } catch (error) {
293
+ // Log for observability
294
+ console.error("Task failed:", error);
295
+
296
+ // Re-throw to trigger retry (if configured)
297
+ throw error;
298
+ }
299
+ }
300
+ );
301
+ ```
302
+
303
+ ### Cancellation Support (TypeScript)
304
+
305
+ ```typescript
306
+ const render = new Render();
307
+ const controller = new AbortController();
308
+
309
+ // Cancel after timeout
310
+ setTimeout(() => controller.abort(), 30000);
311
+
312
+ try {
313
+ const result = await render.workflows.runTask(
314
+ "my-workflow/long-task",
315
+ [data],
316
+ controller.signal
317
+ );
318
+ } catch (error) {
319
+ if (error instanceof AbortError) {
320
+ console.log("Task cancelled");
321
+ }
322
+ }
323
+ ```
324
+
325
+ ## Environment Variables
326
+
327
+ Required:
328
+ - `RENDER_API_KEY` - Your Render API key
329
+
330
+ Optional:
331
+ - `RENDER_USE_LOCAL_DEV` - Enable local development mode (`true`/`false`)
332
+ - `RENDER_LOCAL_DEV_URL` - Local dev URL (default: `http://localhost:8120`)
333
+
334
+ ## Task Identifier Format
335
+
336
+ Tasks are identified by: `{workflow-slug}/{task-name}`
337
+
338
+ Example: `my-workflow/process-data`
339
+
340
+ The workflow slug comes from your Render Dashboard. The task name is either:
341
+ - The function name (default)
342
+ - A custom name specified in task options
343
+
344
+ ## Limitations
345
+
346
+ - Task instances can run for up to 2 hours
347
+ - Task instances cannot receive incoming network connections
348
+ - All task arguments and return values must be JSON-serializable
349
+ - Workflows currently require early access from Render
@@ -0,0 +1,55 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ api:
5
+ build: .
6
+ ports:
7
+ - "3000:3000"
8
+ env_file:
9
+ - .env
10
+ depends_on:
11
+ db:
12
+ condition: service_healthy
13
+ restart: unless-stopped
14
+
15
+ db:
16
+ image: postgres:16-alpine
17
+ ports:
18
+ - "5432:5432"
19
+ environment:
20
+ POSTGRES_USER: postgres
21
+ POSTGRES_PASSWORD: postgres
22
+ POSTGRES_DB: app
23
+ volumes:
24
+ - postgres_data:/var/lib/postgresql/data
25
+ healthcheck:
26
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
27
+ interval: 5s
28
+ timeout: 5s
29
+ retries: 5
30
+
31
+ # Uncomment for Redis
32
+ # redis:
33
+ # image: redis:7-alpine
34
+ # ports:
35
+ # - "6379:6379"
36
+ # volumes:
37
+ # - redis_data:/data
38
+
39
+ # Uncomment for MinIO (S3-compatible storage)
40
+ # minio:
41
+ # image: minio/minio
42
+ # ports:
43
+ # - "9000:9000"
44
+ # - "9001:9001"
45
+ # environment:
46
+ # MINIO_ROOT_USER: minioadmin
47
+ # MINIO_ROOT_PASSWORD: minioadmin
48
+ # volumes:
49
+ # - minio_data:/data
50
+ # command: server /data --console-address ":9001"
51
+
52
+ volumes:
53
+ postgres_data:
54
+ # redis_data:
55
+ # minio_data:
@@ -0,0 +1,15 @@
1
+ import { drizzle } from "drizzle-orm/postgres-js";
2
+ import postgres from "postgres";
3
+ import * as schema from "./schema";
4
+
5
+ const connectionString = process.env.DATABASE_URL;
6
+
7
+ // Create a placeholder client if DATABASE_URL is not set
8
+ // This allows the app to start without a database for local development
9
+ const client = connectionString
10
+ ? postgres(connectionString)
11
+ : postgres("postgres://placeholder:placeholder@localhost:5432/placeholder");
12
+
13
+ export const db = drizzle(client, { schema });
14
+
15
+ export const isDatabaseConfigured = !!connectionString;
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "drizzle-kit";
2
+
3
+ export default defineConfig({
4
+ schema: "./src/db/schema.ts",
5
+ out: "./drizzle",
6
+ dialect: "postgresql",
7
+ dbCredentials: {
8
+ url: process.env.DATABASE_URL!,
9
+ },
10
+ });
@@ -0,0 +1,12 @@
1
+ import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
2
+
3
+ export const users = pgTable("users", {
4
+ id: serial("id").primaryKey(),
5
+ name: text("name").notNull(),
6
+ email: text("email").notNull().unique(),
7
+ createdAt: timestamp("created_at").defaultNow().notNull(),
8
+ updatedAt: timestamp("updated_at").defaultNow().notNull(),
9
+ });
10
+
11
+ // Add more tables here as needed
12
+ // export const posts = pgTable("posts", { ... });
@@ -0,0 +1,15 @@
1
+ # Application
2
+ NODE_ENV=development
3
+ PORT=3000
4
+
5
+ # Database
6
+ DATABASE_URL=postgresql://user:password@localhost:5432/dbname
7
+
8
+ # External APIs
9
+ # API_KEY=your-api-key-here
10
+
11
+ # Storage (S3/MinIO)
12
+ # S3_ENDPOINT=http://localhost:9000
13
+ # S3_ACCESS_KEY=minioadmin
14
+ # S3_SECRET_KEY=minioadmin
15
+ # S3_BUCKET=uploads
@@ -0,0 +1 @@
1
+ # App package
@@ -0,0 +1,12 @@
1
+ from pydantic_settings import BaseSettings
2
+
3
+
4
+ class Settings(BaseSettings):
5
+ app_name: str = "{{PROJECT_NAME}}"
6
+ database_url: str = ""
7
+
8
+ class Config:
9
+ env_file = ".env"
10
+
11
+
12
+ settings = Settings()
@@ -0,0 +1,16 @@
1
+ from sqlalchemy import create_engine
2
+ from sqlalchemy.orm import sessionmaker, declarative_base
3
+ from app.config import settings
4
+
5
+ engine = create_engine(settings.database_url)
6
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
7
+
8
+ Base = declarative_base()
9
+
10
+
11
+ def get_db():
12
+ db = SessionLocal()
13
+ try:
14
+ yield db
15
+ finally:
16
+ db.close()
@@ -0,0 +1,13 @@
1
+ from sqlalchemy import Column, Integer, String, DateTime
2
+ from sqlalchemy.sql import func
3
+ from app.database import Base
4
+
5
+
6
+ class User(Base):
7
+ __tablename__ = "users"
8
+
9
+ id = Column(Integer, primary_key=True, index=True)
10
+ name = Column(String, nullable=False)
11
+ email = Column(String, unique=True, nullable=False, index=True)
12
+ created_at = Column(DateTime(timezone=True), server_default=func.now())
13
+ updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
@@ -0,0 +1,22 @@
1
+ from fastapi import FastAPI
2
+ from app.config import settings
3
+ from app.database import engine
4
+ from app import models
5
+
6
+ # Create database tables
7
+ models.Base.metadata.create_all(bind=engine)
8
+
9
+ app = FastAPI(
10
+ title=settings.app_name,
11
+ version="0.1.0",
12
+ )
13
+
14
+
15
+ @app.get("/health")
16
+ async def health_check():
17
+ return {"status": "ok"}
18
+
19
+
20
+ @app.get("/")
21
+ async def root():
22
+ return {"message": f"Welcome to {settings.app_name}"}