Gary, el Pingüino Artefacto
Gary, el Pingüino Artefacto
Explore posts from servers
CDCloudflare Developers
Created by Gary, el Pingüino Artefacto on 3/23/2025 in #workers-help
Caching for Workers
Hi, I set up a custom and simple worker to allow access for public files but deny access for user uploaded files.
export default {
async fetch(request, env, ctx): Promise<Response> {
const url = new URL(request.url)
const key = url.pathname.slice(1)

if (request.method !== "GET") {
return new Response("Method not allowed", { status: 405 })
}

if (!key) {
return new Response("No key provided", { status: 400 })
}

const cache = caches.default
const cacheKey = new Request(url.toString(), request)
const cachedResponse = await cache.match(cacheKey)
if (cachedResponse) {
return cachedResponse
}

if (key.startsWith("uploads")) {
return new Response("Uploaded files are not available for download", { status: 404 })
}

const object = await env.BUCKET.get(key)
if (!object) {
return new Response("Not found", { status: 404 })
}

const headers = new Headers()
object.writeHttpMetadata(headers)
headers.set("etag", object.httpEtag)
headers.set("Content-Type", object.httpMetadata?.contentType ?? "application/octet-stream")
headers.set("Content-Length", object.size.toString())
headers.set("Cache-Control", "public, max-age=3600")
headers.set("Vary", "Accept-Encoding")

const response = new Response(object.body, { headers })
ctx.waitUntil(cache.put(cacheKey, response.clone()))
return response
},
} satisfies ExportedHandler<Env>
export default {
async fetch(request, env, ctx): Promise<Response> {
const url = new URL(request.url)
const key = url.pathname.slice(1)

if (request.method !== "GET") {
return new Response("Method not allowed", { status: 405 })
}

if (!key) {
return new Response("No key provided", { status: 400 })
}

const cache = caches.default
const cacheKey = new Request(url.toString(), request)
const cachedResponse = await cache.match(cacheKey)
if (cachedResponse) {
return cachedResponse
}

if (key.startsWith("uploads")) {
return new Response("Uploaded files are not available for download", { status: 404 })
}

const object = await env.BUCKET.get(key)
if (!object) {
return new Response("Not found", { status: 404 })
}

const headers = new Headers()
object.writeHttpMetadata(headers)
headers.set("etag", object.httpEtag)
headers.set("Content-Type", object.httpMetadata?.contentType ?? "application/octet-stream")
headers.set("Content-Length", object.size.toString())
headers.set("Cache-Control", "public, max-age=3600")
headers.set("Vary", "Accept-Encoding")

const response = new Response(object.body, { headers })
ctx.waitUntil(cache.put(cacheKey, response.clone()))
return response
},
} satisfies ExportedHandler<Env>
But for every time a request is made, a worker runs. I'm trying to cache the file for 1 hour to avoid the invocation but it doesn't seems to work. Enabling public access via a custom domain isn't an option because I don't want user uploaded to be accessed. Thanks 🙂
7 replies
HHono
Created by Gary, el Pingüino Artefacto on 2/26/2025 in #help
Typing nested controlled
Hi, how can I get this param typed?
import { Hono } from "hono"

// reviews controller
export const reviewsController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:reviewId", (c) => {
const bookId = c.req.param("bookId") // This is not typed. :(
const reviewId = c.req.param("reviewId")
return c.json({ bookId, reviewId })
})

// books controller
export const booksController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.post("/", (c) => c.json({ message: "Hello, world!" }))
.put("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.delete("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.route("/:bookId/reviews", reviewsController)

// main controller
export const controller = new Hono().route("/api/books", booksController)
import { Hono } from "hono"

// reviews controller
export const reviewsController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:reviewId", (c) => {
const bookId = c.req.param("bookId") // This is not typed. :(
const reviewId = c.req.param("reviewId")
return c.json({ bookId, reviewId })
})

// books controller
export const booksController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.post("/", (c) => c.json({ message: "Hello, world!" }))
.put("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.delete("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.route("/:bookId/reviews", reviewsController)

// main controller
export const controller = new Hono().route("/api/books", booksController)
I know this is not recommended https://hono.dev/docs/guides/best-practices but I have a 1000+ nested controlled that I need to refactor
9 replies
TTCTheo's Typesafe Cult
Created by Gary, el Pingüino Artefacto on 2/24/2025 in #questions
Migrating from a Create Next App to TurboRepo
Hi, does someone have an extensive guide or video of how to migrate to TurboRepo? I'm new in TurboRepo and I'm kinda lost. I read some blogs but they only move the entire folder into an app and I'm not sure if this is right. What happends then to the prettier/eslint/other config files?
4 replies
DTDrizzle Team
Created by Gary, el Pingüino Artefacto on 2/19/2025 in #help
mapWith not getting called on extras
Hi
const users = await db.query.users.findMany({
limit: 5,
columns: {
id: true,
givenName: true,
familyName: true,
},
extras: (user) => ({
imageUrl: sql`${db
.select({ id: userImages.fileId })
.from(userImages)
.where(eq(userImages.userId, user.id))
.limit(1)}`
.mapWith((x) => (x ? getUploadUrl(x) : null)) // I'm not getting called :(
.as("imageUrl"),
}),
})

console.log(users)
const users = await db.query.users.findMany({
limit: 5,
columns: {
id: true,
givenName: true,
familyName: true,
},
extras: (user) => ({
imageUrl: sql`${db
.select({ id: userImages.fileId })
.from(userImages)
.where(eq(userImages.userId, user.id))
.limit(1)}`
.mapWith((x) => (x ? getUploadUrl(x) : null)) // I'm not getting called :(
.as("imageUrl"),
}),
})

console.log(users)
1 replies
TtRPC
Created by Gary, el Pingüino Artefacto on 1/5/2025 in #❓-help
Type safe errors?
Hi, it's there a way to make something like this in Trpc?
import { useQuery } from "@tanstack/react-query"
import type { InferResponseType } from "hono"

import { honoClient } from "@/utils/hono"

const request = honoClient.api.hono.auth["get-session"].$post
type SuccessResponse = InferResponseType<typeof request, 200>
type ErrorResponse = InferResponseType<typeof request, 400>

export const useAuthSessionQuery = () => {
const query = useQuery<SuccessResponse, ErrorResponse>({
queryKey: ["useAuthSessionQuery"],
queryFn: async ({ signal }) => {
const response = await request({}, { init: { signal } })
if (response.status === 400) throw await response.json()
if (response.status === 500) throw new Error("Internal server error")
return await response.json()
},
})

return query
}
import { useQuery } from "@tanstack/react-query"
import type { InferResponseType } from "hono"

import { honoClient } from "@/utils/hono"

const request = honoClient.api.hono.auth["get-session"].$post
type SuccessResponse = InferResponseType<typeof request, 200>
type ErrorResponse = InferResponseType<typeof request, 400>

export const useAuthSessionQuery = () => {
const query = useQuery<SuccessResponse, ErrorResponse>({
queryKey: ["useAuthSessionQuery"],
queryFn: async ({ signal }) => {
const response = await request({}, { init: { signal } })
if (response.status === 400) throw await response.json()
if (response.status === 500) throw new Error("Internal server error")
return await response.json()
},
})

return query
}
The idea is to get also the query.error prop also type safe based on the output of the procedure
7 replies
DTDrizzle Team
Created by Gary, el Pingüino Artefacto on 1/3/2025 in #help
OpenTelemetry?
Hi, it's there way to enable OpenTelemetry on drizzle? I found some comments in this discord but couldn't find a way
2 replies
HHono
Created by Gary, el Pingüino Artefacto on 1/3/2025 in #help
Type safety in middleware?
Hi, it's there a way to get the types right here?
import { Hono } from "hono"
import { hc } from "hono/client"

export const honoApp = new Hono()
.use(async (c, next) => {
await next()
if (Math.random() > 0.5) {
return c.json({ authenticated: false }, 401)
}
})
.get("/", (c) => {
if (Math.random() > 0.5) {
return c.json({ message: "ok" }, 200)
}
return c.json({ error: "internal error" }, 500)
})

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _honoClient = hc<typeof honoApp>("/")
type HonoClient = typeof _honoClient

const createHonoClient = (...args: Parameters<typeof hc>): HonoClient => hc<typeof honoApp>(...args)

export const honoClient = createHonoClient("/")
honoClient.index.$get().then(async (res) => {
const data = await res.json()
console.log(data)
})
import { Hono } from "hono"
import { hc } from "hono/client"

export const honoApp = new Hono()
.use(async (c, next) => {
await next()
if (Math.random() > 0.5) {
return c.json({ authenticated: false }, 401)
}
})
.get("/", (c) => {
if (Math.random() > 0.5) {
return c.json({ message: "ok" }, 200)
}
return c.json({ error: "internal error" }, 500)
})

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _honoClient = hc<typeof honoApp>("/")
type HonoClient = typeof _honoClient

const createHonoClient = (...args: Parameters<typeof hc>): HonoClient => hc<typeof honoApp>(...args)

export const honoClient = createHonoClient("/")
honoClient.index.$get().then(async (res) => {
const data = await res.json()
console.log(data)
})
I expected data to be
const data: {
message: string;
} | {
error: string;
} | {
authenticated: boolean;
}
const data: {
message: string;
} | {
error: string;
} | {
authenticated: boolean;
}
But actually it is
const data: {
message: string;
} | {
error: string;
}
const data: {
message: string;
} | {
error: string;
}
12 replies
HHono
Created by Gary, el Pingüino Artefacto on 1/3/2025 in #help
OpenTelemetry?
Hi, does anyone know how to easily implement OpenTelemetry with Hono? Without creating spans manually
5 replies
DTDrizzle Team
Created by Gary, el Pingüino Artefacto on 11/28/2024 in #help
Recommended way for managing Postgres functions
Hi, I need to create multiple Postgres Functions, and they are very likely to change multiple times in the following days/weeks. I read that the recommended way is to create an empty migration file and add the sql there. That means for every change, I would need to create a new empty migration file. I was time about creating them on a directory with raw .sql files and creating them every time the project starts. With this way, a migration file isn't needed. Is this a good idea or may be go with the empty migration?
1 replies
DTDrizzle Team
Created by Gary, el Pingüino Artefacto on 11/16/2024 in #help
Brand ids?
Hi, it's possible to mapped a primary key to a branded type from effect?
export type UserId = string & Brand.Brand<"UserId">
export const UserId = Brand.nominal<UserId>()

export const usersTable = pgTable("users", {
// Possible?
id: uuid().primaryKey().transform((value) => UserId(value)),
createdAt: timestamp().notNull().defaultNow(),
givenName: varchar({ length: 64 }).notNull(),
familyName: varchar({ length: 64 }).notNull(),
})
export type UserId = string & Brand.Brand<"UserId">
export const UserId = Brand.nominal<UserId>()

export const usersTable = pgTable("users", {
// Possible?
id: uuid().primaryKey().transform((value) => UserId(value)),
createdAt: timestamp().notNull().defaultNow(),
givenName: varchar({ length: 64 }).notNull(),
familyName: varchar({ length: 64 }).notNull(),
})
9 replies
DTDrizzle Team
Created by Gary, el Pingüino Artefacto on 11/1/2024 in #help
Conditional batch on Neon Batch API?
Hi, it's possible to do something like:
const deleteImages = true // can be true or false

await db.batch([
db.update(users).set({ ... }).where(...),
deleteImages ? db.delete(userImages).where(...) : false
])
const deleteImages = true // can be true or false

await db.batch([
db.update(users).set({ ... }).where(...),
deleteImages ? db.delete(userImages).where(...) : false
])
3 replies
DTDrizzle Team
Created by Gary, el Pingüino Artefacto on 10/14/2024 in #help
Applying drizzle migrations on Vercel with Hono
Hi, I'm trying to apply that migrations on the directory like this:
await migrate(drizzleDb, {
migrationsFolder: "./src/databases/tenants/migrations",
})
await migrate(drizzleDb, {
migrationsFolder: "./src/databases/tenants/migrations",
})
I'm using Nextjs, deployed on Vercel with a hono api. The path is /api/hono/tenants/create. But every time I get this error: Error: Can't find meta/_journal.json file at /var/task/.next/server/app/api/hono/[[...route]]/route.js:2467:42887 at qp (/var/task/.next/server/app/api/hono/[[...route]]/route.js:2467:43323) at /var/task/.next/server/app/api/hono/[[...route]]/route.js:2467:44022 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async o (/var/task/.next/server/chunks/636.js:67:10412) at async Function.P [as begin] (/var/task/.next/server/chunks/636.js:67:9981) at async qf (/var/task/.next/server/app/api/hono/[[...route]]/route.js:2467:43460) I have tried absolute urls, relative, using path.join, etc. Thanks for the help 😄
2 replies
TTCTheo's Typesafe Cult
Created by Gary, el Pingüino Artefacto on 10/14/2024 in #questions
Applying drizzle migrations with Hono
Hi, I'm trying to apply that migrations on the directory like this:
await migrate(drizzleDb, {
migrationsFolder: "./src/databases/tenants/migrations",
})
await migrate(drizzleDb, {
migrationsFolder: "./src/databases/tenants/migrations",
})
I'm using Nextjs, deployed on Vercel with a hono api. The path is /api/hono/tenants/create. But every time I get this error: Error: Can't find meta/_journal.json file at /var/task/.next/server/app/api/hono/[[...route]]/route.js:2467:42887 at qp (/var/task/.next/server/app/api/hono/[[...route]]/route.js:2467:43323) at /var/task/.next/server/app/api/hono/[[...route]]/route.js:2467:44022 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async o (/var/task/.next/server/chunks/636.js:67:10412) at async Function.P [as begin] (/var/task/.next/server/chunks/636.js:67:9981) at async qf (/var/task/.next/server/app/api/hono/[[...route]]/route.js:2467:43460) I have tried absolute urls, relative, using path.join, etc. Thanks for the help 😄
3 replies
CCConvex Community
Created by Gary, el Pingüino Artefacto on 9/10/2024 in #support-community
How to aggregate documents (more than 16K)?
No description
21 replies
CCConvex Community
Created by Gary, el Pingüino Artefacto on 9/8/2024 in #support-community
The filter helper is limited by the 16384 documents scanned?
No description
2 replies
CCConvex Community
Created by Gary, el Pingüino Artefacto on 8/28/2024 in #support-community
Spamming function calls on stream OpenAI responses
Hi, I was looking at the convex-ai-chat repo and found this https://github.com/get-convex/convex-ai-chat/blob/main/convex/serve.ts#L70
const stream = await openai.chat.completions.create({
model: OPENAI_MODEL,
stream: true,
messages: [
{
role: "system",
content:
"Answer the user question based on the provided documents " +
"or report that the question cannot be answered based on " +
"these documents. Keep the answer informative but brief, " +
"do not enumerate all possibilities.",
},
...(relevantDocuments.map(({ text }) => ({
role: "system",
content: "Relevant document:\n\n" + text,
})) as ChatCompletionMessageParam[]),
...(messages.map(({ isViewer, text }) => ({
role: isViewer ? "user" : "assistant",
content: text,
})) as ChatCompletionMessageParam[]),
],
});
let text = "";
for await (const { choices } of stream) {
const replyDelta = choices[0].delta.content;
if (typeof replyDelta === "string" && replyDelta.length > 0) {
text += replyDelta;
await ctx.runMutation(internal.serve.updateBotMessage, {
messageId,
text,
});
}
}
const stream = await openai.chat.completions.create({
model: OPENAI_MODEL,
stream: true,
messages: [
{
role: "system",
content:
"Answer the user question based on the provided documents " +
"or report that the question cannot be answered based on " +
"these documents. Keep the answer informative but brief, " +
"do not enumerate all possibilities.",
},
...(relevantDocuments.map(({ text }) => ({
role: "system",
content: "Relevant document:\n\n" + text,
})) as ChatCompletionMessageParam[]),
...(messages.map(({ isViewer, text }) => ({
role: isViewer ? "user" : "assistant",
content: text,
})) as ChatCompletionMessageParam[]),
],
});
let text = "";
for await (const { choices } of stream) {
const replyDelta = choices[0].delta.content;
if (typeof replyDelta === "string" && replyDelta.length > 0) {
text += replyDelta;
await ctx.runMutation(internal.serve.updateBotMessage, {
messageId,
text,
});
}
}
Isn't a function trigger every time the a new token gets streamed?
2 replies
TTCTheo's Typesafe Cult
Created by Gary, el Pingüino Artefacto on 8/9/2024 in #questions
Extending React Markdown with any component
Hi, I'm trying to implement charts into the markdown langauge. Currently, I'm using Mermaid for some basic charts but I want to implement my own. For example:
pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15
pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15
This markdown code will render a pie chart using mermaid. How can I implement something to render my own react components? I know mermaid is open source and I could look into the code, but I want to first ask is someone knows an easier way. Also, I will be could to implement any react component because some cool functionally will be unlock.
2 replies
DTDrizzle Team
Created by Gary, el Pingüino Artefacto on 8/7/2024 in #help
Get table definition?
How can I get a SQL to create a table based on a table. For example:
const files = pgTable(
"files",
{
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { mode: "date" }).notNull().defaultNow(),
url: text("url").notNull(),
size: decimal("size"),
contentType: text("content_type"),
originalName: text("original_name"),
path: text("path").notNull().default(""),
},
(table) => ({
pathIdx: index().on(table.path),
})
)


const definition = getTableDefinition(files);
const files = pgTable(
"files",
{
id: uuid("id").primaryKey().defaultRandom(),
createdAt: timestamp("created_at", { mode: "date" }).notNull().defaultNow(),
url: text("url").notNull(),
size: decimal("size"),
contentType: text("content_type"),
originalName: text("original_name"),
path: text("path").notNull().default(""),
},
(table) => ({
pathIdx: index().on(table.path),
})
)


const definition = getTableDefinition(files);
Definition should be something like this:
CREATE TABLE IF NOT EXISTS "files" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"url" text NOT NULL,
"size" numeric,
"content_type" text,
"original_name" text,
"path" text DEFAULT '' NOT NULL
);
CREATE TABLE IF NOT EXISTS "files" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"url" text NOT NULL,
"size" numeric,
"content_type" text,
"original_name" text,
"path" text DEFAULT '' NOT NULL
);
2 replies
TTCTheo's Typesafe Cult
Created by Gary, el Pingüino Artefacto on 7/31/2024 in #questions
Loading server components on the client
Have anyone tried loading server components in the client. Maybe something like this:
<ServerComponentLoader
Component={MyComponent}
someRandomProp="213"
anotherRandomProp={123}
/>
<ServerComponentLoader
Component={MyComponent}
someRandomProp="213"
anotherRandomProp={123}
/>
Or even with children:
<ServerComponentLoader
Component={CreatePostDialog}
suggestedTitle="Some title"
>
<Button>Create post</Button>
</ServerComponentLoader>
<ServerComponentLoader
Component={CreatePostDialog}
suggestedTitle="Some title"
>
<Button>Create post</Button>
</ServerComponentLoader>
2 replies
DTDrizzle Team
Created by Gary, el Pingüino Artefacto on 6/3/2024 in #help
Helpers for querying in the new PostGIS Geometry type
Hi, I was playing around with the new types and I wondered if there are some utility helpers for querying the point. For example:
const stores = pgTable("stores", {
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(),
location: geometry("location", { type: 'point', srid: 4326 }),
});

const nearStores = await db
.select({ id: stores.id, name: stores.name })
.from(stores)
.where(ST_DWithin(stores.location, [some_lon, some_lat], 1000))
const stores = pgTable("stores", {
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(),
location: geometry("location", { type: 'point', srid: 4326 }),
});

const nearStores = await db
.select({ id: stores.id, name: stores.name })
.from(stores)
.where(ST_DWithin(stores.location, [some_lon, some_lat], 1000))
The SQL should be something like this:
SELECT id, name
FROM stores
WHERE ST_DWithin(location, ST_MakePoint(some_lon, some_lat)::geography, 1000);
SELECT id, name
FROM stores
WHERE ST_DWithin(location, ST_MakePoint(some_lon, some_lat)::geography, 1000);
Thanks 🙂
3 replies