commit
1c33189fca
|
|
@ -65,7 +65,7 @@ import type { TaylorDatabase } from "./types.js";
|
|||
export const queryBuilder = createQueryBuilder<TaylorDatabase>({
|
||||
baseUrl: process.env.TAYLORDB_BASE_URL!,
|
||||
baseId: process.env.TAYLORDB_SERVER_ID!,
|
||||
apiKey: process.env.TAYLORDB_API_TOKEN!,
|
||||
apiKey: "",
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -174,7 +174,6 @@ This template is designed to deploy to TaylorDB's platform using the included `t
|
|||
**Environment Variables Required:**
|
||||
|
||||
- `TAYLORDB_BASE_URL`
|
||||
- `TAYLORDB_API_TOKEN`
|
||||
- `TAYLORDB_SERVER_ID`
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import express from "express";
|
||||
import cors from "cors";
|
||||
import cookieParser from "cookie-parser";
|
||||
import * as trpcExpress from "@trpc/server/adapters/express";
|
||||
import { appRouter } from "./router.js";
|
||||
import { createContext } from "./trpc.js";
|
||||
|
|
@ -7,6 +8,9 @@ import { createContext } from "./trpc.js";
|
|||
const app = express();
|
||||
const PORT = process.env.PORT || 3001;
|
||||
|
||||
// Parse cookies
|
||||
app.use(cookieParser());
|
||||
|
||||
// Enable CORS for frontend (adjust origin in production)
|
||||
app.use(
|
||||
cors({
|
||||
|
|
|
|||
|
|
@ -14,13 +14,15 @@
|
|||
"start": "node dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@taylordb/query-builder": "^0.10.3",
|
||||
"@taylordb/query-builder": "^0.11.4",
|
||||
"@trpc/server": "^11.8.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^5.2.1",
|
||||
"zod": "^4.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cookie-parser": "^1.4.10",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^24.10.1",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,39 @@ import { router, publicProcedure } from "../trpc";
|
|||
*
|
||||
* Another example sub-router showing a different domain.
|
||||
* Demonstrates relationships (author references users).
|
||||
*
|
||||
* Example using TaylorDB queryBuilder (available via ctx.queryBuilder):
|
||||
*
|
||||
* // Query all posts
|
||||
* const posts = await ctx.queryBuilder.from("posts").select("*");
|
||||
*
|
||||
* // Query with filters
|
||||
* const publishedPosts = await ctx.queryBuilder
|
||||
* .from("posts")
|
||||
* .where({ published: true })
|
||||
* .select("*");
|
||||
*
|
||||
* // Create a new post
|
||||
* const newPost = await ctx.queryBuilder
|
||||
* .from("posts")
|
||||
* .insert({
|
||||
* title: "My Title",
|
||||
* content: "My Content",
|
||||
* authorId: 1,
|
||||
* published: false
|
||||
* });
|
||||
*
|
||||
* // Update a post
|
||||
* const updatedPost = await ctx.queryBuilder
|
||||
* .from("posts")
|
||||
* .where({ id: 1 })
|
||||
* .update({ published: true });
|
||||
*
|
||||
* // Delete a post
|
||||
* await ctx.queryBuilder
|
||||
* .from("posts")
|
||||
* .where({ id: 1 })
|
||||
* .delete();
|
||||
*/
|
||||
|
||||
// In-memory store for demonstration
|
||||
|
|
@ -36,9 +69,9 @@ export const postsRouter = router({
|
|||
published: z.boolean().optional(),
|
||||
authorId: z.number().optional(),
|
||||
})
|
||||
.optional()
|
||||
.optional(),
|
||||
)
|
||||
.query(({ input }) => {
|
||||
.query(({ input, ctx }) => {
|
||||
let result = posts;
|
||||
|
||||
if (input?.published !== undefined) {
|
||||
|
|
@ -88,7 +121,7 @@ export const postsRouter = router({
|
|||
title: z.string().min(1).max(200).optional(),
|
||||
content: z.string().min(1).optional(),
|
||||
published: z.boolean().optional(),
|
||||
})
|
||||
}),
|
||||
)
|
||||
.mutation(({ input }) => {
|
||||
const post = posts.find((p) => p.id === input.id);
|
||||
|
|
|
|||
|
|
@ -1,319 +0,0 @@
|
|||
import { createQueryBuilder } from "@taylordb/query-builder";
|
||||
import type { TaylorDatabase } from "./types.js";
|
||||
|
||||
/**
|
||||
* TaylorDB Query Builder Instance
|
||||
*
|
||||
* This is the main query builder instance configured with your TaylorDB credentials.
|
||||
* Use this to perform all database operations in a type-safe manner.
|
||||
*/
|
||||
export const queryBuilder = createQueryBuilder<TaylorDatabase>({
|
||||
baseUrl: process.env.TAYLORDB_BASE_URL!,
|
||||
baseId: process.env.TAYLORDB_SERVER_ID!,
|
||||
apiKey: process.env.TAYLORDB_API_TOKEN!,
|
||||
});
|
||||
|
||||
/**
|
||||
* ============================================================================
|
||||
* Example Query Functions
|
||||
* ============================================================================
|
||||
*
|
||||
* Below are example patterns for common database operations.
|
||||
* Replace these with your own functions based on your actual schema.
|
||||
*
|
||||
* For comprehensive examples, see: /docs/TAYLORDB_QUERY_REFERENCE.md
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// READ Operations (Queries)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Example: Get all records from a table
|
||||
*
|
||||
* @example
|
||||
* export async function getAllUsers() {
|
||||
* return await queryBuilder
|
||||
* .selectFrom("users")
|
||||
* .select(["id", "name", "email", "createdAt"])
|
||||
* .orderBy("createdAt", "desc")
|
||||
* .execute();
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Get a single record by ID
|
||||
*
|
||||
* @example
|
||||
* export async function getUserById(id: number) {
|
||||
* return await queryBuilder
|
||||
* .selectFrom("users")
|
||||
* .where("id", "=", id)
|
||||
* .executeTakeFirst();
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Get records with filtering
|
||||
*
|
||||
* @example
|
||||
* export async function getActiveUsers() {
|
||||
* return await queryBuilder
|
||||
* .selectFrom("users")
|
||||
* .where("status", "=", "active")
|
||||
* .orderBy("name", "asc")
|
||||
* .execute();
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Get records with date range filtering
|
||||
*
|
||||
* @example
|
||||
* export async function getRecordsInDateRange(startDate: string, endDate: string) {
|
||||
* return await queryBuilder
|
||||
* .selectFrom("records")
|
||||
* .where("date", ">=", ["exactDay", startDate])
|
||||
* .where("date", "<=", ["exactDay", endDate])
|
||||
* .orderBy("date", "asc")
|
||||
* .execute();
|
||||
* }
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// CREATE Operations (Insert)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Example: Insert a new record
|
||||
*
|
||||
* @example
|
||||
* export async function createUser(data: { name: string; email: string }) {
|
||||
* return await queryBuilder
|
||||
* .insertInto("users")
|
||||
* .values({
|
||||
* name: data.name,
|
||||
* email: data.email,
|
||||
* status: "active",
|
||||
* })
|
||||
* .executeTakeFirst();
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Insert with single-select field
|
||||
*
|
||||
* Note: Single-select fields must be wrapped in an array
|
||||
*
|
||||
* @example
|
||||
* export async function createTask(data: { title: string; priority: "low" | "medium" | "high" }) {
|
||||
* return await queryBuilder
|
||||
* .insertInto("tasks")
|
||||
* .values({
|
||||
* title: data.title,
|
||||
* priority: [data.priority], // Wrap in array for single-select
|
||||
* })
|
||||
* .executeTakeFirst();
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Insert with computed fields
|
||||
*
|
||||
* @example
|
||||
* export async function createOrder(data: { quantity: number; pricePerUnit: number }) {
|
||||
* const totalPrice = data.quantity * data.pricePerUnit;
|
||||
*
|
||||
* return await queryBuilder
|
||||
* .insertInto("orders")
|
||||
* .values({
|
||||
* quantity: data.quantity,
|
||||
* pricePerUnit: data.pricePerUnit,
|
||||
* totalPrice: totalPrice,
|
||||
* })
|
||||
* .executeTakeFirst();
|
||||
* }
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// UPDATE Operations
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Example: Update a record
|
||||
*
|
||||
* @example
|
||||
* export async function updateUser(id: number, data: { name?: string; email?: string }) {
|
||||
* return await queryBuilder
|
||||
* .update("users")
|
||||
* .set(data)
|
||||
* .where("id", "=", id)
|
||||
* .execute();
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Update with conditional recalculation
|
||||
*
|
||||
* @example
|
||||
* export async function updateOrder(id: number, data: { quantity?: number; pricePerUnit?: number }) {
|
||||
* // Fetch current record to compute total
|
||||
* const currentOrder = await queryBuilder
|
||||
* .selectFrom("orders")
|
||||
* .select(["quantity", "pricePerUnit"])
|
||||
* .where("id", "=", id)
|
||||
* .executeTakeFirst();
|
||||
*
|
||||
* if (!currentOrder) {
|
||||
* throw new Error("Order not found");
|
||||
* }
|
||||
*
|
||||
* const newQuantity = data.quantity ?? currentOrder.quantity ?? 0;
|
||||
* const newPrice = data.pricePerUnit ?? currentOrder.pricePerUnit ?? 0;
|
||||
* const totalPrice = newQuantity * newPrice;
|
||||
*
|
||||
* return await queryBuilder
|
||||
* .update("orders")
|
||||
* .set({
|
||||
* ...data,
|
||||
* totalPrice,
|
||||
* })
|
||||
* .where("id", "=", id)
|
||||
* .execute();
|
||||
* }
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// DELETE Operations
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Example: Delete a single record
|
||||
*
|
||||
* @example
|
||||
* export async function deleteUser(id: number) {
|
||||
* return await queryBuilder
|
||||
* .deleteFrom("users")
|
||||
* .where("id", "=", id)
|
||||
* .execute();
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Delete multiple records by IDs
|
||||
*
|
||||
* @example
|
||||
* export async function deleteUsers(ids: number[]) {
|
||||
* return await queryBuilder
|
||||
* .deleteFrom("users")
|
||||
* .where("id", "hasAnyOf", ids)
|
||||
* .execute();
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Delete with condition
|
||||
*
|
||||
* @example
|
||||
* export async function deleteInactiveUsers() {
|
||||
* return await queryBuilder
|
||||
* .deleteFrom("users")
|
||||
* .where("status", "=", "inactive")
|
||||
* .execute();
|
||||
* }
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// AGGREGATION Operations (Manual)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Example: Calculate statistics
|
||||
*
|
||||
* @example
|
||||
* export async function getUserStats() {
|
||||
* const users = await queryBuilder
|
||||
* .selectFrom("users")
|
||||
* .select(["age"])
|
||||
* .execute();
|
||||
*
|
||||
* if (users.length === 0) {
|
||||
* return { count: 0, average: null, min: null, max: null };
|
||||
* }
|
||||
*
|
||||
* const ages = users.map(u => u.age).filter((a): a is number => a !== undefined);
|
||||
*
|
||||
* return {
|
||||
* count: ages.length,
|
||||
* average: ages.reduce((a, b) => a + b, 0) / ages.length,
|
||||
* min: Math.min(...ages),
|
||||
* max: Math.max(...ages),
|
||||
* };
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example: Sum totals for a date
|
||||
*
|
||||
* @example
|
||||
* export async function getTotalSalesForDate(date: string) {
|
||||
* const sales = await queryBuilder
|
||||
* .selectFrom("sales")
|
||||
* .select(["amount", "quantity"])
|
||||
* .where("date", "=", ["exactDay", date])
|
||||
* .execute();
|
||||
*
|
||||
* return {
|
||||
* totalAmount: sales.reduce((sum, s) => sum + (s.amount ?? 0), 0),
|
||||
* totalQuantity: sales.reduce((sum, s) => sum + (s.quantity ?? 0), 0),
|
||||
* };
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* ============================================================================
|
||||
* Query Builder Quick Reference
|
||||
* ============================================================================
|
||||
*
|
||||
* SELECT:
|
||||
* - .selectFrom("tableName")
|
||||
* - .select(["field1", "field2"])
|
||||
* - .execute() // Returns array
|
||||
* - .executeTakeFirst() // Returns single record or undefined
|
||||
*
|
||||
* WHERE:
|
||||
* - .where("field", "=", value)
|
||||
* - .where("field", ">", value)
|
||||
* - .where("field", "hasAnyOf", [value1, value2])
|
||||
* - .where("date", ">=", ["exactDay", "2024-01-01"])
|
||||
*
|
||||
* ORDER BY:
|
||||
* - .orderBy("field", "asc")
|
||||
* - .orderBy("field", "desc")
|
||||
*
|
||||
* INSERT:
|
||||
* - .insertInto("tableName")
|
||||
* - .values({ field1: value1, field2: value2 })
|
||||
* - .executeTakeFirst()
|
||||
*
|
||||
* UPDATE:
|
||||
* - .update("tableName")
|
||||
* - .set({ field1: value1 })
|
||||
* - .where("id", "=", id)
|
||||
* - .execute()
|
||||
*
|
||||
* DELETE:
|
||||
* - .deleteFrom("tableName")
|
||||
* - .where("id", "=", id)
|
||||
* - .execute()
|
||||
*
|
||||
* Field Types:
|
||||
* - Text: string
|
||||
* - Number: number
|
||||
* - Date: ["exactDay", "YYYY-MM-DD"]
|
||||
* - Single Select: ["option"]
|
||||
* - Multi Select: ["opt1", "opt2"]
|
||||
* - Boolean: true/false
|
||||
*
|
||||
* For comprehensive examples, see: /docs/TAYLORDB_QUERY_REFERENCE.md
|
||||
*/
|
||||
|
|
@ -1,15 +1,30 @@
|
|||
import { createQueryBuilder } from "@taylordb/query-builder";
|
||||
import { initTRPC } from "@trpc/server";
|
||||
import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
|
||||
import { TaylorDatabase } from "./taylordb/types";
|
||||
|
||||
/**
|
||||
* Create context for each tRPC request
|
||||
* This is where you can add user session, database clients, etc.
|
||||
*/
|
||||
export const createContext = ({ req, res }: CreateExpressContextOptions) => {
|
||||
// Extract app_access_token from cookies
|
||||
const appAccessToken = req.cookies?.app_access_token;
|
||||
|
||||
if (!appAccessToken) {
|
||||
throw new Error("Unauthorized: app_access_token cookie is required");
|
||||
}
|
||||
|
||||
const queryBuilder = createQueryBuilder<TaylorDatabase>({
|
||||
baseUrl: process.env.TAYLORDB_BASE_URL!,
|
||||
baseId: process.env.TAYLORDB_SERVER_ID!,
|
||||
apiKey: appAccessToken,
|
||||
});
|
||||
|
||||
return {
|
||||
req,
|
||||
res,
|
||||
// Add any shared context here (e.g., database client, user session)
|
||||
queryBuilder,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import type { TaylorDatabase } from "./types.js";
|
|||
export const queryBuilder = createQueryBuilder<TaylorDatabase>({
|
||||
baseUrl: process.env.TAYLORDB_BASE_URL!,
|
||||
baseId: process.env.TAYLORDB_SERVER_ID!,
|
||||
apiKey: process.env.TAYLORDB_API_TOKEN!,
|
||||
apiKey: "",
|
||||
});
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,9 @@ importers:
|
|||
'@trpc/server':
|
||||
specifier: ^11.8.1
|
||||
version: 11.8.1(typescript@5.9.3)
|
||||
cookie-parser:
|
||||
specifier: ^1.4.7
|
||||
version: 1.4.7
|
||||
cors:
|
||||
specifier: ^2.8.5
|
||||
version: 2.8.5
|
||||
|
|
@ -154,6 +157,9 @@ importers:
|
|||
specifier: ^4.3.5
|
||||
version: 4.3.5
|
||||
devDependencies:
|
||||
'@types/cookie-parser':
|
||||
specifier: ^1.4.10
|
||||
version: 1.4.10(@types/express@5.0.6)
|
||||
'@types/cors':
|
||||
specifier: ^2.8.19
|
||||
version: 2.8.19
|
||||
|
|
@ -1315,6 +1321,11 @@ packages:
|
|||
'@types/connect@3.4.38':
|
||||
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
|
||||
|
||||
'@types/cookie-parser@1.4.10':
|
||||
resolution: {integrity: sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==}
|
||||
peerDependencies:
|
||||
'@types/express': '*'
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
|
||||
|
||||
|
|
@ -1569,6 +1580,13 @@ packages:
|
|||
convert-source-map@2.0.0:
|
||||
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
|
||||
|
||||
cookie-parser@1.4.7:
|
||||
resolution: {integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
|
||||
|
||||
cookie-signature@1.2.2:
|
||||
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
|
||||
engines: {node: '>=6.6.0'}
|
||||
|
|
@ -3638,6 +3656,10 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
|
||||
'@types/cookie-parser@1.4.10(@types/express@5.0.6)':
|
||||
dependencies:
|
||||
'@types/express': 5.0.6
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
dependencies:
|
||||
'@types/node': 24.10.1
|
||||
|
|
@ -3942,6 +3964,13 @@ snapshots:
|
|||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
cookie-parser@1.4.7:
|
||||
dependencies:
|
||||
cookie: 0.7.2
|
||||
cookie-signature: 1.0.6
|
||||
|
||||
cookie-signature@1.0.6: {}
|
||||
|
||||
cookie-signature@1.2.2: {}
|
||||
|
||||
cookie@0.7.2: {}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ services:
|
|||
env:
|
||||
vars:
|
||||
TAYLORDB_BASE_URL: vars.TAYLORDB_INTERNAL_BASE_URL
|
||||
TAYLORDB_API_TOKEN: secrets.TAYLORDB_API_TOKEN
|
||||
TAYLORDB_SERVER_ID: vars.TAYLORDB_SERVER_ID
|
||||
FRONTEND_URL: routing.client.url
|
||||
|
||||
|
|
@ -31,7 +30,6 @@ services:
|
|||
env:
|
||||
vars:
|
||||
TAYLORDB_BASE_URL: vars.TAYLORDB_INTERNAL_BASE_URL
|
||||
TAYLORDB_API_TOKEN: secrets.TAYLORDB_API_TOKEN
|
||||
TAYLORDB_SERVER_ID: vars.TAYLORDB_SERVER_ID
|
||||
FRONTEND_URL: routing.client.url
|
||||
|
||||
|
|
@ -41,7 +39,6 @@ services:
|
|||
env:
|
||||
vars:
|
||||
TAYLORDB_BASE_URL: vars.TAYLORDB_INTERNAL_BASE_URL
|
||||
TAYLORDB_API_TOKEN: secrets.TAYLORDB_API_TOKEN
|
||||
TAYLORDB_SERVER_ID: vars.TAYLORDB_SERVER_ID
|
||||
FRONTEND_URL: routing.client.url
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user