@everystack/mcp 0.2.2 → 0.3.0
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/LICENSE +681 -0
- package/README.md +45 -10
- package/dist/adding-database.md +169 -0
- package/dist/admin.md +81 -0
- package/dist/auth.md +115 -0
- package/dist/aws-setup.md +276 -0
- package/dist/cli.md +108 -0
- package/dist/client-api.md +145 -0
- package/dist/core.md +196 -0
- package/dist/deployment.md +146 -0
- package/dist/events.md +87 -0
- package/dist/first-run.md +100 -0
- package/dist/getting-started.md +75 -0
- package/dist/handler-options.md +114 -0
- package/dist/images.md +93 -0
- package/dist/index.cjs +23726 -0
- package/dist/jobs.md +97 -0
- package/dist/logging.md +91 -0
- package/dist/plugins.md +68 -0
- package/dist/project-claude-md.md +102 -0
- package/dist/query-protocol.md +129 -0
- package/dist/schema-patterns.md +167 -0
- package/dist/security-device.md +99 -0
- package/dist/security.md +270 -0
- package/dist/ssr.md +82 -0
- package/dist/storage.md +63 -0
- package/dist/testing.md +118 -0
- package/package.json +26 -14
- package/src/gates/detectors/embedded-data-bundle.ts +58 -0
- package/src/gates/detectors/hand-written-migration.ts +42 -0
- package/src/gates/detectors/secret-in-public-env.ts +41 -0
- package/src/gates/engine.ts +80 -0
- package/src/gates/registry.ts +25 -0
- package/src/gates/telemetry.ts +143 -0
- package/src/gates/types.ts +70 -0
- package/src/governance/cli.ts +193 -0
- package/src/governance/grounding.ts +344 -0
- package/src/index.ts +97 -50
- package/src/prompts/claude-md.ts +90 -0
- package/src/prompts/governance-setup.ts +85 -0
- package/src/prompts/index.ts +4 -0
- package/src/prompts/new-app.ts +4 -1
- package/src/resources/project-claude-md.md +69 -94
- package/src/tools/index.ts +6 -39
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Setting Up AWS for everystack
|
|
2
|
+
|
|
3
|
+
AWS (Amazon Web Services) rents you computers on the internet. When you deploy your app, it runs on AWS servers so anyone in the world can use it. This guide walks you through creating an account and giving your computer permission to deploy.
|
|
4
|
+
|
|
5
|
+
## What does everystack use on AWS?
|
|
6
|
+
|
|
7
|
+
Your app is made up of a few building blocks. Each one is an AWS service:
|
|
8
|
+
|
|
9
|
+
| Service | What it does | Plain English | Tier | Typical cost |
|
|
10
|
+
|---------|-------------|---------------|------|-------------|
|
|
11
|
+
| **S3** | File storage | Holds your app's files (HTML, CSS, JavaScript, images) | V1+ | Pennies/month |
|
|
12
|
+
| **CloudFront** | Content delivery network | Makes your app load fast for users worldwide | V1+ | Free tier covers most small apps |
|
|
13
|
+
| **Lambda** | Serverless compute | Runs your server code only when someone visits | V1+ | Free tier: 1M requests/month |
|
|
14
|
+
| **RDS** | Managed database | Stores your users, posts, and data (Aurora Serverless) | V2+ | ~$15/month minimum |
|
|
15
|
+
| **SQS** | Message queue | Processes background tasks like sending emails | V3 | Pennies/month |
|
|
16
|
+
|
|
17
|
+
**Total cost for a small app with low traffic:** Under $5/month for V1, ~$20/month for V2+ (the database is the main cost).
|
|
18
|
+
|
|
19
|
+
AWS has a **free tier** for the first 12 months that covers most of what everystack uses at low traffic. You will not be charged anything significant while learning.
|
|
20
|
+
|
|
21
|
+
## Step 1: Create an AWS account
|
|
22
|
+
|
|
23
|
+
1. Go to https://aws.amazon.com/free
|
|
24
|
+
2. Click **Create a Free Account**
|
|
25
|
+
3. You will need:
|
|
26
|
+
- An email address
|
|
27
|
+
- A credit card (required for verification, but free tier usage will not be charged)
|
|
28
|
+
- A phone number for verification
|
|
29
|
+
4. Choose the **Basic Support** plan (free)
|
|
30
|
+
5. After account creation, sign in to the AWS Console at https://console.aws.amazon.com to confirm it works
|
|
31
|
+
|
|
32
|
+
## Step 2: Create an IAM user for everystack
|
|
33
|
+
|
|
34
|
+
Your AWS account has a "root user" (the email you signed up with). The root user has unlimited power over your account. You should never use it for daily work. Instead, create a dedicated user just for everystack.
|
|
35
|
+
|
|
36
|
+
**IAM** stands for Identity and Access Management. It controls who can do what in your AWS account.
|
|
37
|
+
|
|
38
|
+
### Create the user
|
|
39
|
+
|
|
40
|
+
1. Sign in to the **AWS Console** at https://console.aws.amazon.com
|
|
41
|
+
2. In the top search bar, type **IAM** and click on it
|
|
42
|
+
3. Click **Users** in the left sidebar
|
|
43
|
+
4. Click **Create user**
|
|
44
|
+
5. Enter the user name: `everystack-dev`
|
|
45
|
+
6. Click **Next**
|
|
46
|
+
|
|
47
|
+
### Create the permissions policy
|
|
48
|
+
|
|
49
|
+
This policy gives the user the permissions SST needs to deploy your app, scoped to your app's resources. Every statement targets your app name prefix so this user can't touch other apps in the same account.
|
|
50
|
+
|
|
51
|
+
7. Select **Attach policies directly**
|
|
52
|
+
8. Click **Create policy** (this opens a new tab)
|
|
53
|
+
9. Click the **JSON** tab
|
|
54
|
+
10. Delete anything in the editor and paste this (replacing the placeholders — see table below):
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"Version": "2012-10-17",
|
|
59
|
+
"Statement": [
|
|
60
|
+
{
|
|
61
|
+
"Sid": "SSM",
|
|
62
|
+
"Effect": "Allow",
|
|
63
|
+
"Action": "ssm:*",
|
|
64
|
+
"Resource": [
|
|
65
|
+
"arn:aws:ssm:{REGION}:{ACCOUNT_ID}:parameter/sst/*",
|
|
66
|
+
"arn:aws:ssm:{REGION}:{ACCOUNT_ID}:parameter/{APP_NAME}/*"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"Sid": "S3",
|
|
71
|
+
"Effect": "Allow",
|
|
72
|
+
"Action": "s3:*",
|
|
73
|
+
"Resource": [
|
|
74
|
+
"arn:aws:s3:::{APP_NAME}-*-*",
|
|
75
|
+
"arn:aws:s3:::{APP_NAME}-*-*/*",
|
|
76
|
+
"arn:aws:s3:::sst-asset-*",
|
|
77
|
+
"arn:aws:s3:::sst-asset-*/*",
|
|
78
|
+
"arn:aws:s3:::sst-state-*",
|
|
79
|
+
"arn:aws:s3:::sst-state-*/*"
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"Sid": "S3Protect",
|
|
84
|
+
"Effect": "Deny",
|
|
85
|
+
"Action": [
|
|
86
|
+
"s3:DeleteBucket",
|
|
87
|
+
"s3:PutBucketAcl",
|
|
88
|
+
"s3:PutBucketVersioning",
|
|
89
|
+
"s3:PutBucketEncryption",
|
|
90
|
+
"s3:PutBucketLogging"
|
|
91
|
+
],
|
|
92
|
+
"Resource": "*"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"Sid": "CF",
|
|
96
|
+
"Effect": "Allow",
|
|
97
|
+
"Action": [
|
|
98
|
+
"cloudfront:*",
|
|
99
|
+
"cloudfront-keyvaluestore:*"
|
|
100
|
+
],
|
|
101
|
+
"Resource": "*"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"Sid": "Lambda",
|
|
105
|
+
"Effect": "Allow",
|
|
106
|
+
"Action": "lambda:*",
|
|
107
|
+
"Resource": "arn:aws:lambda:{REGION}:{ACCOUNT_ID}:function:{APP_NAME}-*"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"Sid": "LambdaGlobal",
|
|
111
|
+
"Effect": "Allow",
|
|
112
|
+
"Action": [
|
|
113
|
+
"lambda:CreateEventSourceMapping",
|
|
114
|
+
"lambda:DeleteEventSourceMapping",
|
|
115
|
+
"lambda:GetEventSourceMapping",
|
|
116
|
+
"lambda:ListEventSourceMappings",
|
|
117
|
+
"lambda:UpdateEventSourceMapping",
|
|
118
|
+
"lambda:ListFunctions",
|
|
119
|
+
"lambda:GetFunctionConfiguration",
|
|
120
|
+
"lambda:TagResource",
|
|
121
|
+
"lambda:UntagResource",
|
|
122
|
+
"lambda:ListTags"
|
|
123
|
+
],
|
|
124
|
+
"Resource": "*"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"Sid": "SQS",
|
|
128
|
+
"Effect": "Allow",
|
|
129
|
+
"Action": "sqs:*",
|
|
130
|
+
"Resource": "arn:aws:sqs:{REGION}:{ACCOUNT_ID}:{APP_NAME}-*"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"Sid": "IAM",
|
|
134
|
+
"Effect": "Allow",
|
|
135
|
+
"Action": "iam:*Role*",
|
|
136
|
+
"Resource": [
|
|
137
|
+
"arn:aws:iam::{ACCOUNT_ID}:role/{APP_NAME}-*",
|
|
138
|
+
"arn:aws:iam::{ACCOUNT_ID}:role/SchedulerRole-*"
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"Sid": "Scheduler",
|
|
143
|
+
"Effect": "Allow",
|
|
144
|
+
"Action": [
|
|
145
|
+
"scheduler:CreateScheduleGroup",
|
|
146
|
+
"scheduler:DeleteScheduleGroup",
|
|
147
|
+
"scheduler:GetScheduleGroup",
|
|
148
|
+
"scheduler:ListTagsForResource",
|
|
149
|
+
"scheduler:TagResource",
|
|
150
|
+
"scheduler:UntagResource"
|
|
151
|
+
],
|
|
152
|
+
"Resource": "arn:aws:scheduler:{REGION}:{ACCOUNT_ID}:schedule-group/{APP_NAME}-*"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"Sid": "Logs",
|
|
156
|
+
"Effect": "Allow",
|
|
157
|
+
"Action": "logs:*",
|
|
158
|
+
"Resource": [
|
|
159
|
+
"arn:aws:logs:{REGION}:{ACCOUNT_ID}:log-group:/aws/lambda/{APP_NAME}-*",
|
|
160
|
+
"arn:aws:logs:{REGION}:{ACCOUNT_ID}:log-group:/aws/lambda/{APP_NAME}-*:*"
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"Sid": "LogsList",
|
|
165
|
+
"Effect": "Allow",
|
|
166
|
+
"Action": "logs:DescribeLogGroups",
|
|
167
|
+
"Resource": "arn:aws:logs:{REGION}:{ACCOUNT_ID}:log-group:*"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"Sid": "Discovery",
|
|
171
|
+
"Effect": "Allow",
|
|
172
|
+
"Action": "s3:ListAllMyBuckets",
|
|
173
|
+
"Resource": "*"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"Sid": "STS",
|
|
177
|
+
"Effect": "Allow",
|
|
178
|
+
"Action": "sts:GetCallerIdentity",
|
|
179
|
+
"Resource": "*"
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Replace the placeholders with your values:
|
|
186
|
+
|
|
187
|
+
| Placeholder | Example | Where to find it |
|
|
188
|
+
|-------------|---------|-------------------|
|
|
189
|
+
| `{ACCOUNT_ID}` | `123456789012` | `aws sts get-caller-identity` |
|
|
190
|
+
| `{REGION}` | `us-east-1` | Your default AWS region |
|
|
191
|
+
| `{APP_NAME}` | `myapp` | The `name` in your `sst.config.ts` |
|
|
192
|
+
|
|
193
|
+
If your app creates additional S3 buckets outside the `{APP_NAME}-*` pattern, add them to the S3 statement.
|
|
194
|
+
|
|
195
|
+
11. Click **Next**
|
|
196
|
+
12. Name the policy: `everystack-dev-policy`
|
|
197
|
+
13. Optionally add a description: "Permissions for everystack SST deploys"
|
|
198
|
+
14. Click **Create policy**
|
|
199
|
+
|
|
200
|
+
### Attach the policy to the user
|
|
201
|
+
|
|
202
|
+
15. Go back to the **Create user** tab in your browser
|
|
203
|
+
16. Click the refresh button on the policy list
|
|
204
|
+
17. Search for `everystack-dev-policy`
|
|
205
|
+
18. Check the box next to it
|
|
206
|
+
19. Click **Next**, then **Create user**
|
|
207
|
+
|
|
208
|
+
### Create an access key
|
|
209
|
+
|
|
210
|
+
20. Click on the user name **everystack-dev**
|
|
211
|
+
21. Go to the **Security credentials** tab
|
|
212
|
+
22. Under **Access keys**, click **Create access key**
|
|
213
|
+
23. Select **Command Line Interface (CLI)**
|
|
214
|
+
24. Check the confirmation checkbox, click **Next**, then **Create access key**
|
|
215
|
+
25. **Save both keys now.** You will not see the secret key again after this page:
|
|
216
|
+
- **Access key ID** (looks like: `AKIAIOSFODNN7EXAMPLE`)
|
|
217
|
+
- **Secret access key** (looks like: `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`)
|
|
218
|
+
|
|
219
|
+
## Step 3: Configure your computer
|
|
220
|
+
|
|
221
|
+
Open a terminal and run:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
aws configure
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
It will ask four questions:
|
|
228
|
+
|
|
229
|
+
- **AWS Access Key ID:** Paste the access key ID from Step 2
|
|
230
|
+
- **AWS Secret Access Key:** Paste the secret access key from Step 2
|
|
231
|
+
- **Default region name:** `us-east-1` (recommended, or choose your nearest region)
|
|
232
|
+
- **Default output format:** `json`
|
|
233
|
+
|
|
234
|
+
## Step 4: Verify it works
|
|
235
|
+
|
|
236
|
+
Run this command:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
aws sts get-caller-identity
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
You should see something like:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"UserId": "AIDAIOSFODNN7EXAMPLE",
|
|
247
|
+
"Account": "123456789012",
|
|
248
|
+
"Arn": "arn:aws:iam::123456789012:user/everystack-dev"
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
If you see your account number and `everystack-dev`, your credentials are working. You are ready to deploy.
|
|
253
|
+
|
|
254
|
+
### What each statement does
|
|
255
|
+
|
|
256
|
+
| Statement | Actions | Scope | Purpose |
|
|
257
|
+
|-----------|---------|-------|---------|
|
|
258
|
+
| **SSM** | `ssm:*` | `sst/*` + `{APP_NAME}/*` params | SST state management + app secrets |
|
|
259
|
+
| **S3** | `s3:*` | App buckets + SST state/asset buckets | Media, client bundles, OTA updates, SST state |
|
|
260
|
+
| **S3Protect** | Deny destructive bucket ops | `*` | Prevents `DeleteBucket`, ACL/versioning/encryption/logging changes |
|
|
261
|
+
| **CF** | `cloudfront:*`, `cloudfront-keyvaluestore:*` | `*` | Router distribution + KVS cache versioning |
|
|
262
|
+
| **Lambda** | `lambda:*` | `{APP_NAME}-*` functions | API, Image, Worker Lambdas |
|
|
263
|
+
| **LambdaGlobal** | Event source mappings, list, tags | `*` | SQS-to-Lambda subscriptions, resource discovery |
|
|
264
|
+
| **SQS** | `sqs:*` | `{APP_NAME}-*` queues | Background jobs + dead-letter queues (V3) |
|
|
265
|
+
| **IAM** | `iam:*Role*` | `{APP_NAME}-*` + `SchedulerRole-*` | Lambda execution roles + EventBridge Scheduler role |
|
|
266
|
+
| **Scheduler** | Schedule group CRUD + tags | `{APP_NAME}-*` groups | EventBridge Scheduler for delayed tasks |
|
|
267
|
+
| **Logs** | `logs:*` | `/aws/lambda/{APP_NAME}-*` | CloudWatch log groups for Lambda functions |
|
|
268
|
+
| **LogsList** | `logs:DescribeLogGroups` | `*` | Log group enumeration |
|
|
269
|
+
| **Discovery** | `s3:ListAllMyBuckets` | `*` | CLI resource discovery |
|
|
270
|
+
| **STS** | `sts:GetCallerIdentity` | `*` | Identity verification |
|
|
271
|
+
|
|
272
|
+
**V2-only apps** can omit `SQS` and `Scheduler`. **V1-only apps** can omit `SQS`, `Scheduler`, `IAM`, and `Logs`.
|
|
273
|
+
|
|
274
|
+
## Production hardening
|
|
275
|
+
|
|
276
|
+
For production, read `everystack://security` for a three-profile model that separates infrastructure creation, day-to-day operations, and CI/CD into distinct IAM users with minimal permissions.
|
package/dist/cli.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# CLI Reference
|
|
2
|
+
|
|
3
|
+
> Full command reference for the `everystack` CLI. Import from `@everystack/cli`.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
Read this when deploying, debugging, or managing a deployed everystack app. All commands use AWS IAM credentials (not shared secrets).
|
|
7
|
+
|
|
8
|
+
## Authentication
|
|
9
|
+
|
|
10
|
+
| Operation | Auth Mechanism |
|
|
11
|
+
|-----------|---------------|
|
|
12
|
+
| `db:migrate`, `db:seed`, `console` | IAM -> Lambda invoke |
|
|
13
|
+
| `update --platform web` | IAM -> S3 PutObject + Lambda invoke |
|
|
14
|
+
| `update --platform ios/android` | `EVERYSTACK_TOKEN` -> HTTP POST |
|
|
15
|
+
| `logs:errors`, `logs:query` | IAM -> Lambda invoke |
|
|
16
|
+
| `logs:tail` | IAM -> CloudWatch FilterLogEvents |
|
|
17
|
+
| `cache:purge` | IAM -> CloudFront KVS |
|
|
18
|
+
| `certs:*` | Local only (no network) |
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
### OTA Updates
|
|
23
|
+
```bash
|
|
24
|
+
everystack update --channel production --message "Fix login bug"
|
|
25
|
+
everystack update --channel production --platform ios --message "iOS fix"
|
|
26
|
+
everystack update --channel staging --platform web
|
|
27
|
+
```
|
|
28
|
+
Flags: `--channel` (default: production), `--message`, `--platform` (ios/android/web/all), `--skip-export`.
|
|
29
|
+
|
|
30
|
+
### Database
|
|
31
|
+
```bash
|
|
32
|
+
everystack db:migrate # Run migrations via Lambda
|
|
33
|
+
everystack db:seed # Seed database (dev only)
|
|
34
|
+
everystack db:psql --stage dev -c "SELECT * FROM posts" # Read-only SQL via Lambda
|
|
35
|
+
everystack console --stage dev # Interactive REPL with db + schema
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The REPL has `db`, `schema`, `eq`, `and`, `or`, `gt`, `lt`, `count`, `sum`, `avg`, `sql`, `desc`, `asc` in scope.
|
|
39
|
+
|
|
40
|
+
### Logs
|
|
41
|
+
```bash
|
|
42
|
+
everystack logs:errors --stage dev # Recent errors (DB -> S3 fallback)
|
|
43
|
+
everystack logs:errors --stage dev --limit 50 --level fatal
|
|
44
|
+
everystack logs:tail --stage dev # CloudWatch Lambda output
|
|
45
|
+
everystack logs:tail --stage dev --filter ERROR --since 1h
|
|
46
|
+
everystack logs:query --stage dev --level error --source api
|
|
47
|
+
everystack logs:query --stage dev --traceId "abc123"
|
|
48
|
+
everystack logs:query --stage dev --userId "user_123" --since 30m
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Cache
|
|
52
|
+
```bash
|
|
53
|
+
everystack cache:purge # Global cache bust
|
|
54
|
+
everystack cache:purge --origin web # Per-origin
|
|
55
|
+
everystack cache:purge --path "/api/posts" # Per-path
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Code Signing
|
|
59
|
+
```bash
|
|
60
|
+
everystack certs:generate --output ./certs # Generate RSA key pair
|
|
61
|
+
everystack certs:configure --input ./certs --keyid main # Add to app.json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Channels
|
|
65
|
+
```bash
|
|
66
|
+
everystack channels list
|
|
67
|
+
everystack channels create --name staging
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### SSR Debugging
|
|
71
|
+
```bash
|
|
72
|
+
everystack diag https://myapp.com # Version + cache check
|
|
73
|
+
everystack diag https://myapp.com/page --hydration # Runtime hydration analysis
|
|
74
|
+
everystack analyze:ssr # Static analysis for SSR anti-patterns
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
The CLI auto-discovers resources from `.sst/outputs.json` (written by `sst deploy`). Required outputs:
|
|
80
|
+
```typescript
|
|
81
|
+
return {
|
|
82
|
+
routerUrl: router.url,
|
|
83
|
+
apiFunctionName: api.name,
|
|
84
|
+
updatesBucket: updates.name,
|
|
85
|
+
clientBundlesBucket: clientBundles.name,
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Use `--stage` flag to target specific deployments: reads from `.sst/outputs.{stage}.json`.
|
|
90
|
+
|
|
91
|
+
## Environment Variables
|
|
92
|
+
|
|
93
|
+
| Variable | Purpose |
|
|
94
|
+
|----------|---------|
|
|
95
|
+
| `AWS_REGION` | AWS region (default: us-east-1) |
|
|
96
|
+
| `AWS_PROFILE` | AWS credentials profile |
|
|
97
|
+
| `EVERYSTACK_TOKEN` | JWT for authenticated mobile uploads |
|
|
98
|
+
|
|
99
|
+
## Lambda onAction Integration
|
|
100
|
+
|
|
101
|
+
The CLI invokes Lambda with `_action` payloads. Your handler must dispatch them:
|
|
102
|
+
```typescript
|
|
103
|
+
onAction: async (action, payload) => {
|
|
104
|
+
if (action === 'migrate') return runMigrations(db, './drizzle');
|
|
105
|
+
if (action === 'seed') return runSeed(db, schema);
|
|
106
|
+
// ... console, db:psql, logs:errors, logs:query, etc.
|
|
107
|
+
}
|
|
108
|
+
```
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Client API
|
|
2
|
+
|
|
3
|
+
> Typed query builder for everystack PostgREST APIs. Import from `@everystack/api/client`.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
Read this when building client-side data fetching in React Native, Expo, or web apps.
|
|
7
|
+
|
|
8
|
+
## Setup
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
import { createClient } from '@everystack/api/client';
|
|
12
|
+
|
|
13
|
+
const api = createClient({
|
|
14
|
+
baseUrl: '/api',
|
|
15
|
+
getToken: () => localStorage.getItem('everystack-token'),
|
|
16
|
+
onTokenExpired: async () => {
|
|
17
|
+
const res = await fetch('/api/auth/refresh', {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: { 'Content-Type': 'application/json' },
|
|
20
|
+
body: JSON.stringify({ refreshToken: localStorage.getItem('everystack-refresh') }),
|
|
21
|
+
});
|
|
22
|
+
const { token } = await res.json();
|
|
23
|
+
localStorage.setItem('everystack-token', token);
|
|
24
|
+
return token;
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Options
|
|
30
|
+
|
|
31
|
+
| Option | Type | Description |
|
|
32
|
+
|--------|------|-------------|
|
|
33
|
+
| `baseUrl` | `string` | API base URL (e.g., `/api` or `https://api.example.com`) |
|
|
34
|
+
| `getToken` | `() => string \| null` | Returns current JWT token |
|
|
35
|
+
| `onTokenExpired` | `() => Promise<string>` | Called on 401 to refresh token. Concurrent 401s deduplicated (one refresh per batch) |
|
|
36
|
+
| `headers` | `Record<string, string>` | Additional headers for every request |
|
|
37
|
+
|
|
38
|
+
## Queries (GET)
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// Basic query
|
|
42
|
+
const { data, error } = await api.from('posts').execute();
|
|
43
|
+
|
|
44
|
+
// Filters
|
|
45
|
+
const { data } = await api.from('posts')
|
|
46
|
+
.eq('status', 'published')
|
|
47
|
+
.neq('authorId', blockedUser)
|
|
48
|
+
.gt('createdAt', yesterday)
|
|
49
|
+
.order('createdAt', 'desc')
|
|
50
|
+
.limit(10)
|
|
51
|
+
.offset(20)
|
|
52
|
+
.execute();
|
|
53
|
+
|
|
54
|
+
// Column selection
|
|
55
|
+
const { data } = await api.from('posts').select('id,title,body').execute();
|
|
56
|
+
|
|
57
|
+
// Relation embedding
|
|
58
|
+
const { data } = await api.from('posts').select('*,author(name,email)').execute();
|
|
59
|
+
|
|
60
|
+
// Logical groups
|
|
61
|
+
const { data } = await api.from('posts')
|
|
62
|
+
.or('status.eq.draft,status.eq.pending')
|
|
63
|
+
.execute();
|
|
64
|
+
|
|
65
|
+
// Exact count
|
|
66
|
+
const { data, count } = await api.from('posts')
|
|
67
|
+
.eq('status', 'published')
|
|
68
|
+
.count()
|
|
69
|
+
.execute();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Filter Methods
|
|
73
|
+
|
|
74
|
+
| Method | Example |
|
|
75
|
+
|--------|---------|
|
|
76
|
+
| `.eq(col, val)` | `?col=eq.val` |
|
|
77
|
+
| `.neq(col, val)` | `?col=neq.val` |
|
|
78
|
+
| `.gt(col, val)` | `?col=gt.val` |
|
|
79
|
+
| `.gte(col, val)` | `?col=gte.val` |
|
|
80
|
+
| `.lt(col, val)` | `?col=lt.val` |
|
|
81
|
+
| `.lte(col, val)` | `?col=lte.val` |
|
|
82
|
+
| `.like(col, pattern)` | `?col=like.pattern` |
|
|
83
|
+
| `.ilike(col, pattern)` | `?col=ilike.pattern` |
|
|
84
|
+
| `.is(col, val)` | `?col=is.val` (null/not.null) |
|
|
85
|
+
| `.in(col, vals)` | `?col=in.(a,b,c)` |
|
|
86
|
+
| `.not(col, op, val)` | `?col=not.op.val` |
|
|
87
|
+
| `.or(expr)` | `?or=(a.eq.1,b.eq.2)` |
|
|
88
|
+
| `.select(cols)` | `?select=cols` |
|
|
89
|
+
| `.order(col, dir)` | `?order=col.dir` |
|
|
90
|
+
| `.limit(n)` | `?limit=n` |
|
|
91
|
+
| `.offset(n)` | `?offset=n` |
|
|
92
|
+
| `.count()` | Adds `Prefer: count=exact` |
|
|
93
|
+
|
|
94
|
+
## Mutations
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Insert
|
|
98
|
+
const { data, error } = await api.from('posts')
|
|
99
|
+
.insert({ body: 'Hello world', authorId: user.sub });
|
|
100
|
+
|
|
101
|
+
// Update
|
|
102
|
+
const { data, error } = await api.from('posts')
|
|
103
|
+
.eq('id', postId)
|
|
104
|
+
.update({ body: 'Updated content' });
|
|
105
|
+
|
|
106
|
+
// Delete
|
|
107
|
+
const { data, error } = await api.from('posts')
|
|
108
|
+
.eq('id', postId)
|
|
109
|
+
.delete();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## RPC
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const { data, error } = await api.rpc('timeline', { limit: 20, offset: 0 });
|
|
116
|
+
const { data } = await api.rpc('search', { query: 'hiking trails', limit: 10 });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Response Shape
|
|
120
|
+
|
|
121
|
+
All methods return `{ data, error, count? }`:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
interface ApiResponse<T> {
|
|
125
|
+
data: T | null;
|
|
126
|
+
error: { status: number; message: string; details?: string } | null;
|
|
127
|
+
count?: number; // Only with .count()
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Token Refresh
|
|
132
|
+
|
|
133
|
+
When a request returns 401:
|
|
134
|
+
1. Client calls `onTokenExpired()` to get a new token
|
|
135
|
+
2. If multiple concurrent 401s fire, they all wait for the SAME refresh (deduplication)
|
|
136
|
+
3. Client retries the original request with the new token
|
|
137
|
+
4. If refresh fails, returns `{ data: null, error: { status: 401, ... } }`
|
|
138
|
+
|
|
139
|
+
## Gotchas
|
|
140
|
+
|
|
141
|
+
- `.execute()` is required to send the request. The builder is lazy.
|
|
142
|
+
- `.insert()`, `.update()`, `.delete()` send immediately (no `.execute()` needed).
|
|
143
|
+
- Filters on `.update()` and `.delete()` scope which rows are affected.
|
|
144
|
+
- The client auto-adds `Content-Type: application/json` for mutations.
|
|
145
|
+
- `baseUrl` should NOT include a trailing slash.
|