postbasejs 0.1.0 → 0.1.2
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.
- package/README.md +496 -0
- package/package.json +12 -2
package/README.md
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
# postbasejs
|
|
2
|
+
|
|
3
|
+
The official JavaScript/TypeScript client for [Postbase](https://www.getpostbase.com) — a self-hosted, open-source backend as a service.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/postbasejs)
|
|
6
|
+
[](https://github.com/postbase/postbase/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
> **[getpostbase.com](https://www.getpostbase.com)** · [Documentation](https://www.getpostbase.com/docs) · [GitHub](https://github.com/postbase/postbase)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What is Postbase?
|
|
13
|
+
|
|
14
|
+
Postbase is a self-hosted backend platform built on PostgreSQL. It gives you a database with a REST query API, authentication (password, magic link, OAuth), file storage, and row-level security — all running on your own infrastructure.
|
|
15
|
+
|
|
16
|
+
`postbasejs` is the client SDK for interacting with your Postbase instance from JavaScript or TypeScript apps.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install postbasejs
|
|
24
|
+
# or
|
|
25
|
+
pnpm add postbasejs
|
|
26
|
+
# or
|
|
27
|
+
yarn add postbasejs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createClient } from 'postbasejs'
|
|
36
|
+
|
|
37
|
+
const postbase = createClient(
|
|
38
|
+
'https://your-postbase-instance.com',
|
|
39
|
+
'pb_anon_your_api_key'
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Your **URL** and **anon key** can be found in the API Keys section of your Postbase dashboard.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Database
|
|
48
|
+
|
|
49
|
+
Query your PostgreSQL tables with a fluent, chainable API.
|
|
50
|
+
|
|
51
|
+
### Select
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// Fetch all posts
|
|
55
|
+
const { data, error } = await postbase.from('posts').select('*')
|
|
56
|
+
|
|
57
|
+
// Select specific columns
|
|
58
|
+
const { data } = await postbase.from('posts').select('id, title, created_at')
|
|
59
|
+
|
|
60
|
+
// With filters
|
|
61
|
+
const { data } = await postbase
|
|
62
|
+
.from('posts')
|
|
63
|
+
.select('*')
|
|
64
|
+
.eq('status', 'published')
|
|
65
|
+
.order('created_at', { ascending: false })
|
|
66
|
+
.limit(10)
|
|
67
|
+
|
|
68
|
+
// Get total count
|
|
69
|
+
const { data, count } = await postbase
|
|
70
|
+
.from('posts')
|
|
71
|
+
.select('*', { count: 'exact' })
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Filter operators
|
|
75
|
+
|
|
76
|
+
| Method | SQL equivalent |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `.eq(col, val)` | `col = val` |
|
|
79
|
+
| `.neq(col, val)` | `col != val` |
|
|
80
|
+
| `.gt(col, val)` | `col > val` |
|
|
81
|
+
| `.gte(col, val)` | `col >= val` |
|
|
82
|
+
| `.lt(col, val)` | `col < val` |
|
|
83
|
+
| `.lte(col, val)` | `col <= val` |
|
|
84
|
+
| `.like(col, pattern)` | `col LIKE pattern` |
|
|
85
|
+
| `.ilike(col, pattern)` | `col ILIKE pattern` |
|
|
86
|
+
| `.in(col, values)` | `col IN (values)` |
|
|
87
|
+
| `.is(col, null)` | `col IS NULL` |
|
|
88
|
+
| `.contains(col, val)` | `col @> val` |
|
|
89
|
+
| `.overlaps(col, val)` | `col && val` |
|
|
90
|
+
| `.textSearch(col, query)` | full-text search |
|
|
91
|
+
| `.or(filters)` | `col = val OR col = val` |
|
|
92
|
+
| `.not(col, op, val)` | `NOT col op val` |
|
|
93
|
+
|
|
94
|
+
### Insert
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const { data, error } = await postbase
|
|
98
|
+
.from('posts')
|
|
99
|
+
.insert({ title: 'Hello World', status: 'draft' })
|
|
100
|
+
.select()
|
|
101
|
+
.single()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Update
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const { data, error } = await postbase
|
|
108
|
+
.from('posts')
|
|
109
|
+
.update({ status: 'published' })
|
|
110
|
+
.eq('id', 'post-id')
|
|
111
|
+
.select()
|
|
112
|
+
.single()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Upsert
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const { data, error } = await postbase
|
|
119
|
+
.from('profiles')
|
|
120
|
+
.upsert({ id: 'user-id', username: 'alice' }, { onConflict: 'id' })
|
|
121
|
+
.select()
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Delete
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const { error } = await postbase
|
|
128
|
+
.from('posts')
|
|
129
|
+
.delete()
|
|
130
|
+
.eq('id', 'post-id')
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Single row helpers
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// Errors if not exactly one row
|
|
137
|
+
const { data, error } = await postbase.from('posts').select('*').eq('id', id).single()
|
|
138
|
+
|
|
139
|
+
// Returns null if not found (no error)
|
|
140
|
+
const { data } = await postbase.from('posts').select('*').eq('id', id).maybeSingle()
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Pagination
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// Limit + offset
|
|
147
|
+
const { data } = await postbase.from('posts').select('*').limit(20).offset(40)
|
|
148
|
+
|
|
149
|
+
// Range (inclusive)
|
|
150
|
+
const { data } = await postbase.from('posts').select('*').range(0, 19)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Authentication
|
|
156
|
+
|
|
157
|
+
### Sign up
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const { data, error } = await postbase.auth.signUp({
|
|
161
|
+
email: 'user@example.com',
|
|
162
|
+
password: 'supersecret',
|
|
163
|
+
})
|
|
164
|
+
// data.user, data.session
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Sign in with password
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const { data, error } = await postbase.auth.signInWithPassword({
|
|
171
|
+
email: 'user@example.com',
|
|
172
|
+
password: 'supersecret',
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Magic link (passwordless)
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const { error } = await postbase.auth.signInWithOtp({
|
|
180
|
+
email: 'user@example.com',
|
|
181
|
+
options: { redirectTo: 'https://yourapp.com/dashboard' },
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### OAuth (browser redirect)
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
await postbase.auth.signInWithOAuth({
|
|
189
|
+
provider: 'google', // or 'github', 'discord', etc.
|
|
190
|
+
options: { redirectTo: 'https://yourapp.com/callback' },
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Get current user
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
const { data: { user }, error } = await postbase.auth.getUser()
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Get current session
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const { data: { session }, error } = await postbase.auth.getSession()
|
|
204
|
+
// session.accessToken, session.user, session.expiresAt
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Sign out
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
await postbase.auth.signOut()
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Update user
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
const { data, error } = await postbase.auth.updateUser({
|
|
217
|
+
name: 'Alice',
|
|
218
|
+
metadata: { plan: 'pro' },
|
|
219
|
+
})
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Listen to auth state changes
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const { data: { subscription } } = postbase.auth.onAuthStateChange((event, session) => {
|
|
226
|
+
// event: 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED'
|
|
227
|
+
console.log(event, session)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// Cleanup
|
|
231
|
+
subscription.unsubscribe()
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Admin (service role key required)
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const adminClient = createClient(url, 'pb_service_your_service_key')
|
|
238
|
+
|
|
239
|
+
// List users
|
|
240
|
+
const { data } = await adminClient.auth.admin.listUsers({ page: 1, perPage: 50 })
|
|
241
|
+
|
|
242
|
+
// Create user
|
|
243
|
+
const { data } = await adminClient.auth.admin.createUser({
|
|
244
|
+
email: 'new@example.com',
|
|
245
|
+
password: 'password',
|
|
246
|
+
email_confirm: true,
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
// Update user
|
|
250
|
+
await adminClient.auth.admin.updateUserById(userId, { email: 'new@example.com' })
|
|
251
|
+
|
|
252
|
+
// Delete user
|
|
253
|
+
await adminClient.auth.admin.deleteUser(userId)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Storage
|
|
259
|
+
|
|
260
|
+
### Upload a file
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const { data, error } = await postbase
|
|
264
|
+
.storage
|
|
265
|
+
.from('avatars')
|
|
266
|
+
.upload('user-123.png', file, { contentType: 'image/png' })
|
|
267
|
+
// data.path, data.fullPath
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Get public URL
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const { data: { publicUrl } } = postbase
|
|
274
|
+
.storage
|
|
275
|
+
.from('avatars')
|
|
276
|
+
.getPublicUrl('user-123.png')
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Download a file
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
const { data: blob, error } = await postbase
|
|
283
|
+
.storage
|
|
284
|
+
.from('avatars')
|
|
285
|
+
.download('user-123.png')
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Create a signed URL (temporary access)
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
const { data, error } = await postbase
|
|
292
|
+
.storage
|
|
293
|
+
.from('private-docs')
|
|
294
|
+
.createSignedUrl('report.pdf', 3600) // expires in 1 hour
|
|
295
|
+
// data.signedUrl
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### List files
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
const { data, error } = await postbase
|
|
302
|
+
.storage
|
|
303
|
+
.from('avatars')
|
|
304
|
+
.list('folder/', { limit: 100, sortBy: { column: 'name', order: 'asc' } })
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Delete files
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
const { error } = await postbase
|
|
311
|
+
.storage
|
|
312
|
+
.from('avatars')
|
|
313
|
+
.remove(['user-123.png', 'user-456.png'])
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Move / Copy
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
await postbase.storage.from('docs').move('old-name.pdf', 'new-name.pdf')
|
|
320
|
+
await postbase.storage.from('docs').copy('template.pdf', 'copy.pdf')
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Bucket management
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
// Create
|
|
327
|
+
await postbase.storage.createBucket('avatars', {
|
|
328
|
+
public: true,
|
|
329
|
+
fileSizeLimit: 5 * 1024 * 1024, // 5 MB
|
|
330
|
+
allowedMimeTypes: ['image/png', 'image/jpeg'],
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
// List
|
|
334
|
+
const { data: buckets } = await postbase.storage.listBuckets()
|
|
335
|
+
|
|
336
|
+
// Update
|
|
337
|
+
await postbase.storage.updateBucket('avatars', { public: false })
|
|
338
|
+
|
|
339
|
+
// Delete
|
|
340
|
+
await postbase.storage.deleteBucket('avatars')
|
|
341
|
+
|
|
342
|
+
// Empty (delete all objects)
|
|
343
|
+
await postbase.storage.emptyBucket('avatars')
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## RPC (PostgreSQL functions)
|
|
349
|
+
|
|
350
|
+
Call a stored procedure or function in your project's schema:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
const { data, error } = await postbase.rpc('get_nearby_posts', {
|
|
354
|
+
lat: 37.7749,
|
|
355
|
+
lng: -122.4194,
|
|
356
|
+
radius: 10,
|
|
357
|
+
})
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## SSR (Server-Side Rendering)
|
|
363
|
+
|
|
364
|
+
For Next.js App Router, SvelteKit, Nuxt, or any SSR framework, import from `postbasejs/ssr`. This forwards the user's session cookie to Postbase so that RLS policies apply server-side.
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# No extra install needed — it's included in postbasejs
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Next.js App Router
|
|
371
|
+
|
|
372
|
+
**Server Component:**
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
import { createServerClient } from 'postbasejs/ssr'
|
|
376
|
+
import { cookies } from 'next/headers'
|
|
377
|
+
|
|
378
|
+
export default async function Page() {
|
|
379
|
+
const cookieStore = await cookies()
|
|
380
|
+
|
|
381
|
+
const postbase = createServerClient(
|
|
382
|
+
process.env.NEXT_PUBLIC_POSTBASE_URL!,
|
|
383
|
+
process.env.NEXT_PUBLIC_POSTBASE_ANON_KEY!,
|
|
384
|
+
{
|
|
385
|
+
cookies: {
|
|
386
|
+
getAll: () => cookieStore.getAll(),
|
|
387
|
+
setAll: () => {}, // read-only in server components
|
|
388
|
+
},
|
|
389
|
+
}
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
const { data: posts } = await postbase.from('posts').select('*')
|
|
393
|
+
return <ul>{posts?.map(p => <li key={p.id}>{p.title}</li>)}</ul>
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Middleware (session refresh):**
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
// middleware.ts
|
|
401
|
+
import { createServerClient } from 'postbasejs/ssr'
|
|
402
|
+
import { NextResponse } from 'next/server'
|
|
403
|
+
import type { NextRequest } from 'next/server'
|
|
404
|
+
|
|
405
|
+
export async function middleware(req: NextRequest) {
|
|
406
|
+
const res = NextResponse.next()
|
|
407
|
+
|
|
408
|
+
const postbase = createServerClient(
|
|
409
|
+
process.env.NEXT_PUBLIC_POSTBASE_URL!,
|
|
410
|
+
process.env.NEXT_PUBLIC_POSTBASE_ANON_KEY!,
|
|
411
|
+
{
|
|
412
|
+
cookies: {
|
|
413
|
+
getAll: () => req.cookies.getAll(),
|
|
414
|
+
setAll: (cookies) =>
|
|
415
|
+
cookies.forEach(c => res.cookies.set(c.name, c.value, c.options as any)),
|
|
416
|
+
},
|
|
417
|
+
}
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
await postbase.auth.getSession() // refreshes token if needed
|
|
421
|
+
return res
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Client Component:**
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
'use client'
|
|
429
|
+
import { createBrowserClient } from 'postbasejs/ssr'
|
|
430
|
+
|
|
431
|
+
const postbase = createBrowserClient(
|
|
432
|
+
process.env.NEXT_PUBLIC_POSTBASE_URL!,
|
|
433
|
+
process.env.NEXT_PUBLIC_POSTBASE_ANON_KEY!
|
|
434
|
+
)
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## TypeScript
|
|
440
|
+
|
|
441
|
+
The SDK is fully typed. Pass your row type as a generic for full IntelliSense:
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
interface Post {
|
|
445
|
+
id: string
|
|
446
|
+
title: string
|
|
447
|
+
status: 'draft' | 'published'
|
|
448
|
+
created_at: string
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const { data } = await postbase.from<Post>('posts').select('*').eq('status', 'published')
|
|
452
|
+
// data is Post[] | null
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## Row Level Security (RLS)
|
|
458
|
+
|
|
459
|
+
When a user is signed in, their session JWT is automatically forwarded with every query. Your RLS policies can reference the user via:
|
|
460
|
+
|
|
461
|
+
```sql
|
|
462
|
+
current_setting('postbase.user_id', true) -- the authenticated user's ID
|
|
463
|
+
current_setting('postbase.role', true) -- the user's role
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Example policy — users can only read their own rows:
|
|
467
|
+
|
|
468
|
+
```sql
|
|
469
|
+
CREATE POLICY "own rows" ON posts
|
|
470
|
+
FOR SELECT USING (
|
|
471
|
+
user_id = current_setting('postbase.user_id', true)::uuid
|
|
472
|
+
);
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Environment Variables
|
|
478
|
+
|
|
479
|
+
We recommend storing your Postbase credentials in environment variables:
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
NEXT_PUBLIC_POSTBASE_URL=https://your-postbase-instance.com
|
|
483
|
+
NEXT_PUBLIC_POSTBASE_ANON_KEY=pb_anon_...
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Use your **service role key** (`pb_service_...`) only in server-side code — it bypasses RLS.
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## License
|
|
491
|
+
|
|
492
|
+
MIT — see [LICENSE](https://github.com/postbase/postbase/blob/main/LICENSE).
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
Built with love by the [Postbase](https://www.getpostbase.com) team.
|
package/package.json
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postbasejs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The official JavaScript client for Postbase — self-hosted backend as a service",
|
|
5
|
-
"keywords": ["postbase", "database", "auth", "storage", "backend"],
|
|
5
|
+
"keywords": ["postbase", "database", "auth", "storage", "backend", "self-hosted", "postgresql", "baas"],
|
|
6
|
+
"homepage": "https://www.getpostbase.com",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/harshalone/postbase"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/harshalone/postbase/issues"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "Postbase",
|
|
6
16
|
"main": "./dist/index.js",
|
|
7
17
|
"module": "./dist/index.mjs",
|
|
8
18
|
"types": "./dist/index.d.ts",
|