Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/chronark/zod-bird
Fully typed Tinybird pipes using zod
https://github.com/chronark/zod-bird
Last synced: 3 days ago
JSON representation
Fully typed Tinybird pipes using zod
- Host: GitHub
- URL: https://github.com/chronark/zod-bird
- Owner: chronark
- License: mit
- Created: 2023-05-21T07:59:44.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-10-02T16:23:42.000Z (about 1 month ago)
- Last Synced: 2024-11-06T11:01:57.370Z (10 days ago)
- Language: TypeScript
- Size: 510 KB
- Stars: 156
- Watchers: 3
- Forks: 14
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
@chronark/zod-bird
Fully typed Tinybird pipes using zod
- typesafe
- handles building the url params for you
- easy transformation of resulting data
- built in cache directives for nextjs
- built in retry logic for ratelimited requests```ts
import { Tinybird } from "@chronark/zod-bird";
import { z } from "zod";const tb = new Tinybird({ token: "token" });
export const getEvents = tb.buildPipe({
pipe: "get_events__v1",
parameters: z.object({
tenantId: z.string(),
}),
data: z.object({
event: z.string(),
time: z.number().transform((t) => new Date(t)),
}),
});const res = await getEvents({ tenantId: "chronark" })
// res.data = {event: string, time: Date}[]
// if no rows found, res.data will be an empty array
```## Install
```
npm i @chronark/zod-bird
```## Ingesting Data
```ts
// lib/tinybird.ts
import { Tinybird } from "./client";
import { z } from "zod";const tb = new Tinybird({ token: process.env.TINYBIRD_TOKEN! });
export const publishEvent = tb.buildIngestEndpoint({
datasource: "events__v1",
event: z.object({
id: z.string(),
tenantId: z.string(),
channelId: z.string(),
time: z.number().int(),
event: z.string(),
content: z.string().optional().default(""),
metadata: z.string().optional().default(""),
}),
});
``````ts
// somewhere.ts
import { publishEvent } from "./lib/tinybird";await publishEvent({
id: "1",
tenantId: "1",
channelId: "1",
time: Date.now(),
event: "test",
content: "test",
metadata: JSON.stringify({ test: "test" }),
});
```Or multiple events in bulk, using a single request to Tinybird.
```ts
// somewhere.ts
import { publishEvent } from "./lib/tinybird";await publishEvent([
{
id: "1",
tenantId: "1",
channelId: "1",
time: Date.now(),
event: "test",
content: "test",
metadata: JSON.stringify({ test: "test" }),
},
{
id: "2",
tenantId: "2",
channelId: "2",
time: Date.now(),
event: "test2",
content: "test2",
metadata: JSON.stringify({ test2: "test2" }),
},
]);
```## Querying Endpoints
```ts
// lib/tinybird.ts
import { Tinybird } from "./client";
import { z } from "zod";const tb = new Tinybird({ token: process.env.TINYBIRD_TOKEN! });
export const getChannelActivity = tb.buildPipe({
pipe: "get_channel_activity__v1",
parameters: z.object({
tenantId: z.string(),
channelId: z.string().optional(),
start: z.number(),
end: z.number().optional(),
granularity: z.enum(["1m", "1h", "1d", "1w", "1M"]),
}),
data: z.object({
time: z.string().transform((t) => new Date(t).getTime()),
count: z
.number()
.nullable()
.optional()
.transform((v) => (typeof v === "number" ? v : 0)),
}),
});
``````ts
// somewhere.ts
import { getChannelActivity } from "./lib/tinybird";const res = await getChannelActivity({
tenantId: "1",
channelId: "1",
start: 123,
end: 1234,
granularity: "1h"
})```
`res` is the response from the tinybird endpoint, but now fully typed and the data has been parsed according to the schema defined in `data`.## Error handling
For `429` (rate limit exceeded) and `500` errors, `zod-bird` automatically retries them ([source code](https://github.com/chronark/zod-bird/blob/52944e5aebcfb547f9ccaf1e3fca03b158953e8f/src/client.ts#L45-L60)).
All other errors will throw an `Exception`, so you should catch it via a try catch:
```typescript
try {
const res = await getEvents({ tenantId: "chronark" })
return res.data
} catch (e) {
console.error(e)
return e.messsage
}
```## Advanced
### Caching
You can add cache directives to each pipe.
#### Disable caching (useful in Next.js where everything is cached by default)
```ts
tb.buildPipe({
pipe: "some_pipe__v1",
parameters: z.object({
hello: z.string()
}),
data: z.object({
response: z.string()
}),
opts: {
cache: "no-store" // <-------- Add this
};
});```
#### Cache for 15 minutes
```ts
tb.buildPipe({
pipe: "some_pipe__v1",
parameters: z.object({
hello: z.string()
}),
data: z.object({
response: z.string()
}),
opts: {
next: {
revalidate: 900 ;// <-------- Add this
}
};
});