oz
oz
Explore posts from servers
TTCTheo's Typesafe Cult
Created by oz on 7/12/2024 in #questions
Best Frontend Testing System?
For my NextJS app what is the BEST software out there for a small freelance team to create tests for our clients projects so as we develop we ensure each input, form, page, drawer, etc have the info and function as they need. Bonus if it connects to github to pass / fail pull requests based on those tests. Also we will be using Vercel for hosting on most if not all platforms if that info is helpful. What do you guys recommend?
14 replies
TTCTheo's Typesafe Cult
Created by oz on 7/12/2024 in #questions
Drizzle where query problems
I am using drizzle. In my below aPI I cant get my where request to correctly get me my restricted products. When i call the API, it says no restrictions found for product id 63905 which is wrong as I have the below row that DOES have that ID???? db obj example sent in next msg
import {
integer,
pgTable,
serial,
text,
timestamp,
jsonb,
boolean,
} from "drizzle-orm/pg-core";

export const restrictionGroupTable = pgTable("restriction_group", {
id: serial("id").primaryKey(),
name: text("name").notNull().unique(),
brands: jsonb("brands").notNull().default("[]"),
products: jsonb("products").notNull().default("[]"),
categories: jsonb("categories").notNull().default("[]"),
zipcodes: jsonb("zipcodes").notNull().default("[]"),
states: jsonb("states").notNull().default("[]"),
enabled: boolean("enabled").notNull().default(false),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at")
.notNull()
.$onUpdate(() => new Date()),
});

export type InsertRestrictionGroup = typeof restrictionGroupTable.$inferInsert;
export type SelectRestrictionGroup = typeof restrictionGroupTable.$inferSelect;
import {
integer,
pgTable,
serial,
text,
timestamp,
jsonb,
boolean,
} from "drizzle-orm/pg-core";

export const restrictionGroupTable = pgTable("restriction_group", {
id: serial("id").primaryKey(),
name: text("name").notNull().unique(),
brands: jsonb("brands").notNull().default("[]"),
products: jsonb("products").notNull().default("[]"),
categories: jsonb("categories").notNull().default("[]"),
zipcodes: jsonb("zipcodes").notNull().default("[]"),
states: jsonb("states").notNull().default("[]"),
enabled: boolean("enabled").notNull().default(false),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at")
.notNull()
.$onUpdate(() => new Date()),
});

export type InsertRestrictionGroup = typeof restrictionGroupTable.$inferInsert;
export type SelectRestrictionGroup = typeof restrictionGroupTable.$inferSelect;
// api/restrictions/[productId]/[brandId]/[categoryId]/route.ts

import { NextRequest, NextResponse } from "next/server";
import { db } from "@/db";
import { restrictionGroupTable } from "@/db/schema";
import { eq, sql, or, inArray } from "drizzle-orm";

export const revalidate = 3600;

export async function GET(
request: NextRequest,
{
params,
}: { params: { productId: string; brandId: string; categoryId: string } },
) {
const { productId, brandId, categoryId } = params;

try {
console.log("Fetching restrictions for:", {
productId,
brandId,
categoryId,
});

const restrictions = await db
.select({
name: restrictionGroupTable.name,
brands: restrictionGroupTable.brands,
categories: restrictionGroupTable.categories,
products: restrictionGroupTable.products,
zipcodes: restrictionGroupTable.zipcodes,
states: restrictionGroupTable.states,
})
.from(restrictionGroupTable)
.where(
or(
inArray(restrictionGroupTable.products, [sql${productId}::jsonb]),
inArray(restrictionGroupTable.brands, [sql${brandId}::jsonb]),
inArray(restrictionGroupTable.categories, [
sql${categoryId}::jsonb,
]),
),
)
.execute();

console.log("Restrictions fetched:", restrictions);

if (restrictions.length === 0) {
console.log(
"No restrictions found for this product, brand, or category.",
);
}

return NextResponse.json(restrictions);
} catch (error) {
console.error("Error fetching restrictions:", error);
return NextResponse.json(
{ error: "Failed to fetch restrictions" },
{ status: 500 },
);
}
}
// api/restrictions/[productId]/[brandId]/[categoryId]/route.ts

import { NextRequest, NextResponse } from "next/server";
import { db } from "@/db";
import { restrictionGroupTable } from "@/db/schema";
import { eq, sql, or, inArray } from "drizzle-orm";

export const revalidate = 3600;

export async function GET(
request: NextRequest,
{
params,
}: { params: { productId: string; brandId: string; categoryId: string } },
) {
const { productId, brandId, categoryId } = params;

try {
console.log("Fetching restrictions for:", {
productId,
brandId,
categoryId,
});

const restrictions = await db
.select({
name: restrictionGroupTable.name,
brands: restrictionGroupTable.brands,
categories: restrictionGroupTable.categories,
products: restrictionGroupTable.products,
zipcodes: restrictionGroupTable.zipcodes,
states: restrictionGroupTable.states,
})
.from(restrictionGroupTable)
.where(
or(
inArray(restrictionGroupTable.products, [sql${productId}::jsonb]),
inArray(restrictionGroupTable.brands, [sql${brandId}::jsonb]),
inArray(restrictionGroupTable.categories, [
sql${categoryId}::jsonb,
]),
),
)
.execute();

console.log("Restrictions fetched:", restrictions);

if (restrictions.length === 0) {
console.log(
"No restrictions found for this product, brand, or category.",
);
}

return NextResponse.json(restrictions);
} catch (error) {
console.error("Error fetching restrictions:", error);
return NextResponse.json(
{ error: "Failed to fetch restrictions" },
{ status: 500 },
);
}
}
18 replies
TTCTheo's Typesafe Cult
Created by oz on 7/9/2024 in #questions
Cost Efficient XL Generation for Large Products
Hey guys, title could have been better, but I am basically trying to setup an XML product feed for my store, which has 20k+ products including variants, and it constantly grows and shrinks throughout each day. I want the feed to rerun every 15 minutes, and I want to host this on vercel. I am worried about serverles costs since this is a function, below is my code with my approach I am doing caching, is this the most efficient way to handle this to reduce costs in Vercel? Is serverless (vercel) hosting bad for this use case?
// app/api/product-feed/route.ts

import { NextResponse } from 'next/server';
import { kv } from '@vercel/kv';
import { BigCommerceClient } from './bigcommerce-client';
import { generateXMLFeed } from './xml-generator';

const CACHE_KEY = 'product-feed-cache';
const CACHE_TTL = 15 * 60; // 15 minutes in seconds

export async function GET() {
try {
// Check cache first
const cachedFeed = await kv.get(CACHE_KEY);
if (cachedFeed) {
return new NextResponse(cachedFeed, {
headers: { 'Content-Type': 'application/xml' },
});
}

// If not in cache, generate new feed
const client = new BigCommerceClient();
const products = await client.getAllProducts();
const xmlFeed = generateXMLFeed(products);

// Cache the new feed
await kv.set(CACHE_KEY, xmlFeed, { ex: CACHE_TTL });

return new NextResponse(xmlFeed, {
headers: { 'Content-Type': 'application/xml' },
});
} catch (error) {
console.error('Error generating product feed:', error);
return new NextResponse('Error generating product feed', { status: 500 });
}
}

// Scheduled job to update cache (run every 15 minutes)
export async function POST() {
try {
const client = new BigCommerceClient();
const products = await client.getAllProducts();
const xmlFeed = generateXMLFeed(products);
await kv.set(CACHE_KEY, xmlFeed, { ex: CACHE_TTL });
return new NextResponse('Cache updated successfully', { status: 200 });
} catch (error) {
console.error('Error updating cache:', error);
return new NextResponse('Error updating cache', { status: 500 });
}
}
// app/api/product-feed/route.ts

import { NextResponse } from 'next/server';
import { kv } from '@vercel/kv';
import { BigCommerceClient } from './bigcommerce-client';
import { generateXMLFeed } from './xml-generator';

const CACHE_KEY = 'product-feed-cache';
const CACHE_TTL = 15 * 60; // 15 minutes in seconds

export async function GET() {
try {
// Check cache first
const cachedFeed = await kv.get(CACHE_KEY);
if (cachedFeed) {
return new NextResponse(cachedFeed, {
headers: { 'Content-Type': 'application/xml' },
});
}

// If not in cache, generate new feed
const client = new BigCommerceClient();
const products = await client.getAllProducts();
const xmlFeed = generateXMLFeed(products);

// Cache the new feed
await kv.set(CACHE_KEY, xmlFeed, { ex: CACHE_TTL });

return new NextResponse(xmlFeed, {
headers: { 'Content-Type': 'application/xml' },
});
} catch (error) {
console.error('Error generating product feed:', error);
return new NextResponse('Error generating product feed', { status: 500 });
}
}

// Scheduled job to update cache (run every 15 minutes)
export async function POST() {
try {
const client = new BigCommerceClient();
const products = await client.getAllProducts();
const xmlFeed = generateXMLFeed(products);
await kv.set(CACHE_KEY, xmlFeed, { ex: CACHE_TTL });
return new NextResponse('Cache updated successfully', { status: 200 });
} catch (error) {
console.error('Error updating cache:', error);
return new NextResponse('Error updating cache', { status: 500 });
}
}
Then I would setup a cron job in vercel
9 replies
TTCTheo's Typesafe Cult
Created by oz on 7/7/2024 in #questions
Route workin in Dev and not Prod?
Hey guys I have a route defined (code below) that in dev works fine, I call it with PUT and it does just that fine. I am using latest NextJS 14 and am hosgting prod env in Vercel. In prod I get:
Request URL:
https://xxxxxx/api/defendants?id=13
Request Method:
PUT
Status Code:
405 Method Not Allowed
Remote Address:
xxxxxxx
Referrer Policy:
strict-origin-when-cross-origin
Request URL:
https://xxxxxx/api/defendants?id=13
Request Method:
PUT
Status Code:
405 Method Not Allowed
Remote Address:
xxxxxxx
Referrer Policy:
strict-origin-when-cross-origin
But the same call works perfectly fine in dev
Request URL:
http://localhost:3000/api/defendants?id=210
Request Method:
PUT
Status Code:
200 OK
Remote Address:
[::1]:3000
Referrer Policy:
strict-origin-when-cross-origin
Request URL:
http://localhost:3000/api/defendants?id=210
Request Method:
PUT
Status Code:
200 OK
Remote Address:
[::1]:3000
Referrer Policy:
strict-origin-when-cross-origin
// /api/defendants.js
import { NextRequest, NextResponse } from "next/server";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export async function GET() {
try {
const defendants = await prisma.defendant.findMany({
select: {
id: true,
companyName: true,
companyAddress: true,
ownerName: true,
ownerAddress: true,
ownerPhone: true,
ownerEmail: true,
},
});

return NextResponse.json({ defendants });
} catch (error) {
console.error("Error fetching defendants:", error);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
} finally {
await prisma.$disconnect();
}
}

export async function PUT(request: NextRequest) {
try {
const data = await request.json();
const updatedDefendant = await prisma.defendant.update({
where: { id: data.id },
data: {
companyName: data.companyName,
companyAddress: data.companyAddress,
ownerName: data.ownerName,
ownerAddress: data.ownerAddress,
ownerPhone: data.ownerPhone,
ownerEmail: data.ownerEmail,
},
});

return NextResponse.json({ defendant: updatedDefendant });
} catch (error) {
console.error("Error updating defendant:", error);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
} finally {
await prisma.$disconnect();
}
}
// /api/defendants.js
import { NextRequest, NextResponse } from "next/server";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export async function GET() {
try {
const defendants = await prisma.defendant.findMany({
select: {
id: true,
companyName: true,
companyAddress: true,
ownerName: true,
ownerAddress: true,
ownerPhone: true,
ownerEmail: true,
},
});

return NextResponse.json({ defendants });
} catch (error) {
console.error("Error fetching defendants:", error);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
} finally {
await prisma.$disconnect();
}
}

export async function PUT(request: NextRequest) {
try {
const data = await request.json();
const updatedDefendant = await prisma.defendant.update({
where: { id: data.id },
data: {
companyName: data.companyName,
companyAddress: data.companyAddress,
ownerName: data.ownerName,
ownerAddress: data.ownerAddress,
ownerPhone: data.ownerPhone,
ownerEmail: data.ownerEmail,
},
});

return NextResponse.json({ defendant: updatedDefendant });
} catch (error) {
console.error("Error updating defendant:", error);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 },
);
} finally {
await prisma.$disconnect();
}
}
6 replies
TTCTheo's Typesafe Cult
Created by oz on 7/3/2024 in #questions
Advice for LARGE Product Embedding for AI?
Hey guys asking for advice. I want to make an AI chat bot. I want it to be able to chat about ANY product we have, but the store I am working with has 10k+ products and more get added / edited everyday. I am using openai, what would be the best way to feed the AI about these products? Would it be crazy expensive?
2 replies
TTCTheo's Typesafe Cult
Created by oz on 6/19/2024 in #questions
Best PDF Parsing Practices?
I have a system that gets a bunch of legal documents. The documents are all for the same legal case type but they are not the same documents, as each lawyer has their own wording, they look and are formatted diff but have the same important info of course. I want to extract certain data. What is the best approach for this? As of now I am leaning towards: Anyscale JSON Mode that returns json object of vals I ask for, thoughts? https://www.anyscale.com/blog/anyscale-endpoints-json-mode-and-function-calling-features ^ Credit to Rauch 🙂 https://github.com/rauchg/next-ai-news As of now I dont see how I can escape AI for this, but want to ensure I maximize the best model/service for this and not waste money on things I dont need (like throwing chatgpt-4o for example which is much more expensive for a model I dont need) Any ideas from anyone who did things similarly?
2 replies
TTCTheo's Typesafe Cult
Created by oz on 6/18/2024 in #questions
UploadThing - Cant pass userId
Hey guys, my lack of knowledge is showing here 😓 I am trying to pass the input prop to pass my user ID but I cant use that prop since I am not really using the base UploadComponent as I followed the nextJS docs and it uses a generator. So it says input is not a valid prop, how do I fix this? /api/uploadthing/core.ts
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { UploadThingError } from "uploadthing/server";

const f = createUploadthing();

// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({ image: { maxFileSize: "4MB" } })
// Set permissions and file types for this FileRoute
.middleware(async ({ req, input }) => {
try {
// This code runs on your server before upload
const userId = input.userId as string | null;

// If you throw, the user will not be able to upload
if (!userId) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId };
} catch (error) {
console.error("Middleware error:", error);
throw new UploadThingError("Internal Server Error");
}
})
.onUploadComplete(async ({ metadata, file }) => {
// This code RUNS ON YOUR SERVER after upload
console.log("Upload complete for userId:", metadata.userId);

console.log("file url", file.url);

// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;
import { createUploadthing, type FileRouter } from "uploadthing/next";
import { UploadThingError } from "uploadthing/server";

const f = createUploadthing();

// FileRouter for your app, can contain multiple FileRoutes
export const ourFileRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({ image: { maxFileSize: "4MB" } })
// Set permissions and file types for this FileRoute
.middleware(async ({ req, input }) => {
try {
// This code runs on your server before upload
const userId = input.userId as string | null;

// If you throw, the user will not be able to upload
if (!userId) throw new UploadThingError("Unauthorized");

// Whatever is returned here is accessible in onUploadComplete as `metadata`
return { userId };
} catch (error) {
console.error("Middleware error:", error);
throw new UploadThingError("Internal Server Error");
}
})
.onUploadComplete(async ({ metadata, file }) => {
// This code RUNS ON YOUR SERVER after upload
console.log("Upload complete for userId:", metadata.userId);

console.log("file url", file.url);

// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
return { uploadedBy: metadata.userId };
}),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;
page.tsx
"use client";

import { UploadButton } from "../../../util/uploadthing";
import { useEffect, useState } from "react";

export default function Home() {
const [userId, setUserId] = useState<string | null>(null);

useEffect(() => {
const fetchUserId = async () => {
const response = await fetch("/api/auth/me/id");
const data = await response.json();
setUserId(data.userId);
};

fetchUserId();
}, []);

return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<UploadButton
endpoint="imageUploader"
input={{ userId }}
onClientUploadComplete={(res) => {
// Do something with the response
console.log("Files: ", res);
alert("Upload Completed");
}}
onUploadError={(error: Error) => {
// Do something with the error.
alert(`ERROR! ${error.message}`);
}}
/>
</main>
);
}
"use client";

import { UploadButton } from "../../../util/uploadthing";
import { useEffect, useState } from "react";

export default function Home() {
const [userId, setUserId] = useState<string | null>(null);

useEffect(() => {
const fetchUserId = async () => {
const response = await fetch("/api/auth/me/id");
const data = await response.json();
setUserId(data.userId);
};

fetchUserId();
}, []);

return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<UploadButton
endpoint="imageUploader"
input={{ userId }}
onClientUploadComplete={(res) => {
// Do something with the response
console.log("Files: ", res);
alert("Upload Completed");
}}
onUploadError={(error: Error) => {
// Do something with the error.
alert(`ERROR! ${error.message}`);
}}
/>
</main>
);
}
22 replies
RRailway
Created by oz on 12/25/2023 in #✋|help
Fiberplane + Railway?`
How would I setup https://fiberplane.com/ for my Springboot app on Railway? Is it even possible?
4 replies
RRailway
Created by oz on 10/5/2023 in #✋|help
How to get Port as Env var?
I basically want to pas in the dynamic port of my service as an environment var, dont see info on that in docs, only for TCP proxy
11 replies
RRailway
Created by oz on 10/5/2023 in #✋|help
Matomo on Railway
Anyone know a good way I can setup Matomo on Railway? I know how to do it on a normal VPS or local, but since I cant interact with console I am unsure how to do this. Any help is appreciated!
46 replies