kaddidlehopper 0.1.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/CONTEXT.md +139 -0
- package/README.md +47 -0
- package/add-ons/ai/README.md +34 -0
- package/add-ons/ai/assets/_dot_env.local.append +13 -0
- package/add-ons/ai/assets/src/components/AIAssistant.tsx +149 -0
- package/add-ons/ai/assets/src/lib/ai-hook.ts +21 -0
- package/add-ons/ai/assets/src/lib/weather-tools.ts +30 -0
- package/add-ons/ai/assets/src/routes/api.chat.ts +94 -0
- package/add-ons/ai/assets/src/routes/chat.css +175 -0
- package/add-ons/ai/assets/src/routes/chat.tsx +141 -0
- package/add-ons/ai/info.json +27 -0
- package/add-ons/ai/package.json +17 -0
- package/add-ons/ai/small-logo.svg +8 -0
- package/dist/cli.js +251 -0
- package/dist/index.js +33 -0
- package/dist/types/cli.d.ts +8 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/types.d.ts +14 -0
- package/dist/types.js +1 -0
- package/examples/blog/README.md +60 -0
- package/examples/blog/assets/content/posts/beach.md +12 -0
- package/examples/blog/assets/content/posts/jungle.md.ejs +12 -0
- package/examples/blog/assets/content/posts/mountains.md.ejs +12 -0
- package/examples/blog/assets/content/posts/snorkeling.md.ejs +12 -0
- package/examples/blog/assets/content/posts/waterfall.md.ejs +12 -0
- package/examples/blog/assets/content-collections.ts +30 -0
- package/examples/blog/assets/public/beach.jpg +0 -0
- package/examples/blog/assets/public/jungle.jpg +0 -0
- package/examples/blog/assets/public/mountains.jpg +0 -0
- package/examples/blog/assets/public/snorkeling.jpg +0 -0
- package/examples/blog/assets/public/waterfall.jpg +0 -0
- package/examples/blog/assets/src/components/Header.tsx +52 -0
- package/examples/blog/assets/src/components/VacayAssistant.tsx +205 -0
- package/examples/blog/assets/src/components/blog-posts.tsx +78 -0
- package/examples/blog/assets/src/components/ui/card.tsx +92 -0
- package/examples/blog/assets/src/lib/blog-ai-hook.ts +25 -0
- package/examples/blog/assets/src/lib/blog-tools.ts +111 -0
- package/examples/blog/assets/src/lib/utils.ts +6 -0
- package/examples/blog/assets/src/routes/__root.tsx +57 -0
- package/examples/blog/assets/src/routes/api.blog-chat.ts +117 -0
- package/examples/blog/assets/src/routes/category.$category.tsx +19 -0
- package/examples/blog/assets/src/routes/index.tsx +19 -0
- package/examples/blog/assets/src/routes/posts.$slug.tsx +63 -0
- package/examples/blog/assets/src/styles.css +138 -0
- package/examples/blog/info.json +43 -0
- package/examples/blog/package.json +23 -0
- package/examples/events/README.md +110 -0
- package/examples/events/assets/content/speakers/andre-costa.md +22 -0
- package/examples/events/assets/content/speakers/hans-mueller.md.ejs +22 -0
- package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +22 -0
- package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +22 -0
- package/examples/events/assets/content/speakers/marie-dubois.md.ejs +20 -0
- package/examples/events/assets/content/speakers/priya-sharma.md.ejs +22 -0
- package/examples/events/assets/content/talks/croissant-lamination-secrets.md +39 -0
- package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +39 -0
- package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +39 -0
- package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +39 -0
- package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +36 -0
- package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +32 -0
- package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +39 -0
- package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +39 -0
- package/examples/events/assets/content-collections.ts +56 -0
- package/examples/events/assets/public/background-1.jpg +0 -0
- package/examples/events/assets/public/background-2.jpg +0 -0
- package/examples/events/assets/public/background-3.jpg +0 -0
- package/examples/events/assets/public/background-4.jpg +0 -0
- package/examples/events/assets/public/conference-logo.png +0 -0
- package/examples/events/assets/public/favicon.ico +0 -0
- package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
- package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
- package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
- package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
- package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
- package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
- package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
- package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
- package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
- package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
- package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
- package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
- package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
- package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
- package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
- package/examples/events/assets/public/tanstack-word-logo-white.svg +1 -0
- package/examples/events/assets/src/components/Header.tsx +59 -0
- package/examples/events/assets/src/components/HeaderNav.tsx +67 -0
- package/examples/events/assets/src/components/HeroCarousel.tsx +61 -0
- package/examples/events/assets/src/components/RemyAssistant.tsx +207 -0
- package/examples/events/assets/src/components/SpeakerCard.tsx +67 -0
- package/examples/events/assets/src/components/TalkCard.tsx +77 -0
- package/examples/events/assets/src/components/ui/card.tsx +92 -0
- package/examples/events/assets/src/lib/conference-ai-hook.ts +26 -0
- package/examples/events/assets/src/lib/conference-tools.ts +210 -0
- package/examples/events/assets/src/lib/model-selection.ts +1 -0
- package/examples/events/assets/src/lib/utils.ts +6 -0
- package/examples/events/assets/src/routes/__root.tsx +70 -0
- package/examples/events/assets/src/routes/api.remy-chat.ts +119 -0
- package/examples/events/assets/src/routes/index.tsx +192 -0
- package/examples/events/assets/src/routes/schedule.index.tsx +274 -0
- package/examples/events/assets/src/routes/speakers.$slug.tsx +122 -0
- package/examples/events/assets/src/routes/speakers.index.tsx +40 -0
- package/examples/events/assets/src/routes/talks.$slug.tsx +116 -0
- package/examples/events/assets/src/routes/talks.index.tsx +40 -0
- package/examples/events/assets/src/styles.css +182 -0
- package/examples/events/info.json +74 -0
- package/examples/events/package.json +23 -0
- package/examples/marketing/README.md +60 -0
- package/examples/marketing/assets/public/logo.png +0 -0
- package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
- package/examples/marketing/assets/src/components/Header.tsx +36 -0
- package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +162 -0
- package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +53 -0
- package/examples/marketing/assets/src/data/motorcycles.ts.ejs +77 -0
- package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +24 -0
- package/examples/marketing/assets/src/lib/motorcycle-tools.ts +42 -0
- package/examples/marketing/assets/src/routes/__root.tsx +57 -0
- package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +78 -0
- package/examples/marketing/assets/src/routes/index.tsx +72 -0
- package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +56 -0
- package/examples/marketing/assets/src/store/motorcycle-assistant.ts +3 -0
- package/examples/marketing/assets/src/styles.css +212 -0
- package/examples/marketing/info.json +38 -0
- package/examples/marketing/package.json +14 -0
- package/examples/resume/README.md +82 -0
- package/examples/resume/assets/content/education/code-school.md +17 -0
- package/examples/resume/assets/content/jobs/freelance.md.ejs +13 -0
- package/examples/resume/assets/content/jobs/initech-junior.md +20 -0
- package/examples/resume/assets/content/jobs/initech-lead.md.ejs +29 -0
- package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +28 -0
- package/examples/resume/assets/content-collections.ts +36 -0
- package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
- package/examples/resume/assets/src/components/Header.tsx +33 -0
- package/examples/resume/assets/src/components/ResumeAssistant.tsx +193 -0
- package/examples/resume/assets/src/components/ui/badge.tsx +46 -0
- package/examples/resume/assets/src/components/ui/card.tsx +92 -0
- package/examples/resume/assets/src/components/ui/checkbox.tsx +30 -0
- package/examples/resume/assets/src/components/ui/hover-card.tsx +44 -0
- package/examples/resume/assets/src/components/ui/separator.tsx +26 -0
- package/examples/resume/assets/src/lib/resume-ai-hook.ts +21 -0
- package/examples/resume/assets/src/lib/resume-tools.ts +165 -0
- package/examples/resume/assets/src/lib/utils.ts +6 -0
- package/examples/resume/assets/src/routes/api.resume-chat.ts +110 -0
- package/examples/resume/assets/src/routes/index.tsx +220 -0
- package/examples/resume/assets/src/styles.css +138 -0
- package/examples/resume/info.json +25 -0
- package/examples/resume/package.json +26 -0
- package/package.json +39 -0
- package/project/base/_dot_claude/skills/content-collections/SKILL.md +505 -0
- package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +410 -0
- package/project/base/_dot_claude/skills/netlify-db/SKILL.md +424 -0
- package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +419 -0
- package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +243 -0
- package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +372 -0
- package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +421 -0
- package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +426 -0
- package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +493 -0
- package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +430 -0
- package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +445 -0
- package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +494 -0
- package/project/base/_dot_gitignore +8 -0
- package/project/base/netlify.toml +7 -0
- package/project/base/package.json +33 -0
- package/project/base/public/favicon.ico +0 -0
- package/project/base/public/tanstack-circle-logo.png +0 -0
- package/project/base/public/tanstack-word-logo-white.svg +1 -0
- package/project/base/src/components/Header.tsx +17 -0
- package/project/base/src/components/HeaderNav.tsx.ejs +179 -0
- package/project/base/src/router.tsx +15 -0
- package/project/base/src/routes/__root.tsx +57 -0
- package/project/base/src/routes/index.tsx +48 -0
- package/project/base/src/styles.css +15 -0
- package/project/base/tsconfig.json +28 -0
- package/project/base/vite.config.ts.ejs +25 -0
- package/project/packages.json +22 -0
- package/scripts/check-outdated-packages.js +421 -0
- package/src/cli.ts +343 -0
- package/src/index.ts +49 -0
- package/src/types.ts +15 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: netlify-functions
|
|
3
|
+
description: Create and deploy Netlify serverless functions and edge functions. Use when implementing API endpoints, server-side logic, background jobs, scheduled tasks, or edge computing on Netlify.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
author: netlify
|
|
7
|
+
version: "1.0"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Netlify Functions
|
|
11
|
+
|
|
12
|
+
Netlify Functions are serverless functions that run on-demand without managing servers. They support JavaScript, TypeScript, and Go.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- API endpoints for your frontend
|
|
17
|
+
- Server-side data processing
|
|
18
|
+
- Third-party API integrations (hiding API keys)
|
|
19
|
+
- Background/async processing
|
|
20
|
+
- Scheduled jobs (cron-like)
|
|
21
|
+
- Edge computing (low-latency responses)
|
|
22
|
+
|
|
23
|
+
## Directory Structure
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
project/
|
|
27
|
+
├── netlify/
|
|
28
|
+
│ └── functions/
|
|
29
|
+
│ ├── hello.ts # → /.netlify/functions/hello
|
|
30
|
+
│ ├── api/
|
|
31
|
+
│ │ └── users.ts # → /.netlify/functions/api-users
|
|
32
|
+
│ └── process-background.ts # Background function
|
|
33
|
+
│ └── edge-functions/
|
|
34
|
+
│ └── geo.ts # Edge function
|
|
35
|
+
├── netlify.toml
|
|
36
|
+
└── package.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Basic Serverless Function (TypeScript)
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// netlify/functions/hello.ts
|
|
43
|
+
import type { Context } from "@netlify/functions";
|
|
44
|
+
|
|
45
|
+
export default async (request: Request, context: Context) => {
|
|
46
|
+
const { name = "World" } = await request.json().catch(() => ({}));
|
|
47
|
+
|
|
48
|
+
return new Response(JSON.stringify({ message: `Hello, ${name}!` }), {
|
|
49
|
+
status: 200,
|
|
50
|
+
headers: { "Content-Type": "application/json" },
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Function with Path Parameters
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// netlify/functions/users.ts
|
|
59
|
+
import type { Context } from "@netlify/functions";
|
|
60
|
+
|
|
61
|
+
export default async (request: Request, context: Context) => {
|
|
62
|
+
const { id } = context.params; // From path like /api/users/:id
|
|
63
|
+
|
|
64
|
+
// Fetch user from database
|
|
65
|
+
const user = await getUser(id);
|
|
66
|
+
|
|
67
|
+
if (!user) {
|
|
68
|
+
return new Response(JSON.stringify({ error: "User not found" }), {
|
|
69
|
+
status: 404,
|
|
70
|
+
headers: { "Content-Type": "application/json" },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return Response.json(user);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Configure custom path in the function
|
|
78
|
+
export const config = {
|
|
79
|
+
path: "/api/users/:id",
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Handling Different HTTP Methods
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// netlify/functions/items.ts
|
|
87
|
+
import type { Context } from "@netlify/functions";
|
|
88
|
+
|
|
89
|
+
export default async (request: Request, context: Context) => {
|
|
90
|
+
const { method } = request;
|
|
91
|
+
|
|
92
|
+
switch (method) {
|
|
93
|
+
case "GET":
|
|
94
|
+
return handleGet(request, context);
|
|
95
|
+
case "POST":
|
|
96
|
+
return handlePost(request, context);
|
|
97
|
+
case "PUT":
|
|
98
|
+
return handlePut(request, context);
|
|
99
|
+
case "DELETE":
|
|
100
|
+
return handleDelete(request, context);
|
|
101
|
+
default:
|
|
102
|
+
return new Response("Method not allowed", { status: 405 });
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
async function handleGet(request: Request, context: Context) {
|
|
107
|
+
const items = await fetchItems();
|
|
108
|
+
return Response.json(items);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function handlePost(request: Request, context: Context) {
|
|
112
|
+
const body = await request.json();
|
|
113
|
+
const newItem = await createItem(body);
|
|
114
|
+
return Response.json(newItem, { status: 201 });
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Environment Variables
|
|
119
|
+
|
|
120
|
+
Access environment variables via `process.env`:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// netlify/functions/api.ts
|
|
124
|
+
export default async (request: Request, context: Context) => {
|
|
125
|
+
const apiKey = process.env.API_KEY;
|
|
126
|
+
|
|
127
|
+
if (!apiKey) {
|
|
128
|
+
return new Response("API key not configured", { status: 500 });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const response = await fetch("https://api.example.com/data", {
|
|
132
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return Response.json(await response.json());
|
|
136
|
+
};
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Set environment variables in Netlify UI or `netlify.toml`:
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
# netlify.toml
|
|
143
|
+
[build.environment]
|
|
144
|
+
API_KEY = "your-api-key" # Better to set in Netlify UI for secrets
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Background Functions
|
|
148
|
+
|
|
149
|
+
For long-running tasks (up to 15 minutes), use background functions:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// netlify/functions/process-background.ts
|
|
153
|
+
// Name MUST end with `-background`
|
|
154
|
+
import type { Context } from "@netlify/functions";
|
|
155
|
+
|
|
156
|
+
export default async (request: Request, context: Context) => {
|
|
157
|
+
const data = await request.json();
|
|
158
|
+
|
|
159
|
+
// Long-running task - client gets 202 immediately
|
|
160
|
+
await processLargeDataset(data);
|
|
161
|
+
|
|
162
|
+
// Response is ignored for background functions
|
|
163
|
+
// Client always receives 202 Accepted immediately
|
|
164
|
+
};
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Naming**: File must be named `*-background.ts` (e.g., `process-background.ts`)
|
|
168
|
+
|
|
169
|
+
## Scheduled Functions
|
|
170
|
+
|
|
171
|
+
Run functions on a schedule (cron):
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// netlify/functions/daily-cleanup.ts
|
|
175
|
+
import type { Config } from "@netlify/functions";
|
|
176
|
+
|
|
177
|
+
export default async () => {
|
|
178
|
+
await cleanupOldRecords();
|
|
179
|
+
console.log("Cleanup completed");
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export const config: Config = {
|
|
183
|
+
schedule: "0 0 * * *", // Run daily at midnight UTC
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Common cron patterns:
|
|
188
|
+
- `"0 * * * *"` - Every hour
|
|
189
|
+
- `"0 0 * * *"` - Daily at midnight
|
|
190
|
+
- `"0 0 * * 0"` - Weekly on Sunday
|
|
191
|
+
- `"*/5 * * * *"` - Every 5 minutes
|
|
192
|
+
|
|
193
|
+
## Edge Functions
|
|
194
|
+
|
|
195
|
+
Edge functions run closer to users for ultra-low latency:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// netlify/edge-functions/geo.ts
|
|
199
|
+
import type { Context } from "@netlify/edge-functions";
|
|
200
|
+
|
|
201
|
+
export default async (request: Request, context: Context) => {
|
|
202
|
+
const { country, city } = context.geo;
|
|
203
|
+
|
|
204
|
+
return new Response(JSON.stringify({
|
|
205
|
+
message: `Hello from ${city}, ${country}!`,
|
|
206
|
+
timestamp: new Date().toISOString(),
|
|
207
|
+
}), {
|
|
208
|
+
headers: { "Content-Type": "application/json" },
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export const config = {
|
|
213
|
+
path: "/api/location",
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Configure in `netlify.toml`:
|
|
218
|
+
|
|
219
|
+
```toml
|
|
220
|
+
[[edge_functions]]
|
|
221
|
+
function = "geo"
|
|
222
|
+
path = "/api/location"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Edge Function vs Serverless Function
|
|
226
|
+
|
|
227
|
+
| Feature | Serverless | Edge |
|
|
228
|
+
|---------|-----------|------|
|
|
229
|
+
| Location | Single region | Global edge |
|
|
230
|
+
| Timeout | 10s (26s on paid) | 50ms |
|
|
231
|
+
| Cold start | Can be slow | Very fast |
|
|
232
|
+
| Use case | Heavy computation | Low-latency responses |
|
|
233
|
+
| Middleware | No | Yes |
|
|
234
|
+
|
|
235
|
+
## Context Object Properties
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
export default async (request: Request, context: Context) => {
|
|
239
|
+
// Request info
|
|
240
|
+
context.ip; // Client IP address
|
|
241
|
+
context.geo.city; // Geo location
|
|
242
|
+
context.geo.country.code; // Country code
|
|
243
|
+
context.geo.subdivision.code; // State/region
|
|
244
|
+
|
|
245
|
+
// Deploy info
|
|
246
|
+
context.site.id; // Site ID
|
|
247
|
+
context.deploy.id; // Deploy ID
|
|
248
|
+
context.deploy.published; // Is this the published deploy?
|
|
249
|
+
|
|
250
|
+
// Function info
|
|
251
|
+
context.requestId; // Unique request ID
|
|
252
|
+
context.params; // URL path parameters
|
|
253
|
+
|
|
254
|
+
// Cookies
|
|
255
|
+
context.cookies.get("session");
|
|
256
|
+
context.cookies.set({ name: "session", value: "abc123" });
|
|
257
|
+
|
|
258
|
+
// Environment
|
|
259
|
+
context.env.get("API_KEY");
|
|
260
|
+
};
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## CORS Configuration
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// netlify/functions/api.ts
|
|
267
|
+
const headers = {
|
|
268
|
+
"Access-Control-Allow-Origin": "*",
|
|
269
|
+
"Access-Control-Allow-Headers": "Content-Type",
|
|
270
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
export default async (request: Request, context: Context) => {
|
|
274
|
+
// Handle preflight
|
|
275
|
+
if (request.method === "OPTIONS") {
|
|
276
|
+
return new Response(null, { status: 204, headers });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Your logic here
|
|
280
|
+
const data = { message: "Hello" };
|
|
281
|
+
|
|
282
|
+
return new Response(JSON.stringify(data), {
|
|
283
|
+
status: 200,
|
|
284
|
+
headers: { ...headers, "Content-Type": "application/json" },
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## netlify.toml Configuration
|
|
290
|
+
|
|
291
|
+
```toml
|
|
292
|
+
[build]
|
|
293
|
+
functions = "netlify/functions"
|
|
294
|
+
|
|
295
|
+
[functions]
|
|
296
|
+
# Set default Node.js version
|
|
297
|
+
node_bundler = "esbuild"
|
|
298
|
+
|
|
299
|
+
# Include files in function bundle
|
|
300
|
+
included_files = ["data/**"]
|
|
301
|
+
|
|
302
|
+
# Function-specific settings
|
|
303
|
+
[functions."api-*"]
|
|
304
|
+
# Increase memory for API functions
|
|
305
|
+
memory = 1024
|
|
306
|
+
|
|
307
|
+
# Edge function declarations
|
|
308
|
+
[[edge_functions]]
|
|
309
|
+
function = "auth"
|
|
310
|
+
path = "/dashboard/*"
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Local Development
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Install Netlify CLI
|
|
317
|
+
npm install -g netlify-cli
|
|
318
|
+
|
|
319
|
+
# Run locally with functions
|
|
320
|
+
netlify dev
|
|
321
|
+
|
|
322
|
+
# Functions available at http://localhost:8888/.netlify/functions/
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Common Patterns
|
|
326
|
+
|
|
327
|
+
### Database Connection
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// netlify/functions/db.ts
|
|
331
|
+
import { neon } from "@neondatabase/serverless";
|
|
332
|
+
|
|
333
|
+
const sql = neon(process.env.DATABASE_URL!);
|
|
334
|
+
|
|
335
|
+
export default async (request: Request) => {
|
|
336
|
+
const users = await sql`SELECT * FROM users LIMIT 10`;
|
|
337
|
+
return Response.json(users);
|
|
338
|
+
};
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Rate Limiting with Blobs
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { getStore } from "@netlify/blobs";
|
|
345
|
+
|
|
346
|
+
export default async (request: Request, context: Context) => {
|
|
347
|
+
const store = getStore("rate-limits");
|
|
348
|
+
const ip = context.ip;
|
|
349
|
+
|
|
350
|
+
const current = await store.get(ip, { type: "json" }) as { count: number; reset: number } | null;
|
|
351
|
+
const now = Date.now();
|
|
352
|
+
|
|
353
|
+
if (current && current.reset > now && current.count >= 100) {
|
|
354
|
+
return new Response("Rate limit exceeded", { status: 429 });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
await store.setJSON(ip, {
|
|
358
|
+
count: (current?.count || 0) + 1,
|
|
359
|
+
reset: current?.reset > now ? current.reset : now + 60000,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Continue with request...
|
|
363
|
+
};
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Limits
|
|
367
|
+
|
|
368
|
+
- **Serverless functions**: 10 second timeout (26s on paid plans)
|
|
369
|
+
- **Background functions**: 15 minute timeout
|
|
370
|
+
- **Edge functions**: 50ms CPU time
|
|
371
|
+
- **Payload size**: 6MB request/response
|
|
372
|
+
- **Memory**: 1024MB default (configurable)
|