You can now create endpoints and functions entirely through the Payload Admin UI - no code required! The system supports two modes:
- File-based: Import existing functions from your es6Api
- Inline Code: Write functions directly in the admin panel
pnpm devVisit: http://localhost:3000/admin
- Navigate to Functions collection
- Click Create New
- Fill in the form (see examples below)
- Save
- Navigate to Routes collection
- Click Create New
- Link to your function
- Save
That's it! Your endpoint is live immediately at the specified path.
Example: Simple Hello World
┌─────────────────────────────────────┐
│ Create New Function │
├─────────────────────────────────────┤
│ Name: helloWorld │
│ Description: Returns hello message │
│ Service: other │
│ Implementation Type: Inline Code │
│ │
│ Function Code: │
│ ┌─────────────────────────────────┐ │
│ │ try { │ │
│ │ const name = req.query.name │ │
│ │ res.json({ │ │
│ │ message: `Hello ${name}!` │ │
│ │ }) │ │
│ │ } catch (error) { │ │
│ │ res.status(500).json({ │ │
│ │ error: error.message │ │
│ │ }) │ │
│ │ } │ │
│ └─────────────────────────────────┘ │
│ │
│ Response Type: JSON │
│ Timeout: 30000 │
│ Is Async: ✓ │
│ Active: ✓ │
│ │
│ [Cancel] [Save] │
└─────────────────────────────────────┘
Create the Route:
┌─────────────────────────────────────┐
│ Create New Route │
├─────────────────────────────────────┤
│ Name: Hello Endpoint │
│ Path: /api/hello │
│ Method: GET │
│ Function: helloWorld (dropdown) │
│ Requires Auth: □ │
│ Active: ✓ │
│ │
│ [Cancel] [Save] │
└─────────────────────────────────────┘
Test it:
curl "http://localhost:3000/api/hello?name=World"
# Response: {"message":"Hello World!"}Example: Knowi Database Query
┌─────────────────────────────────────┐
│ Create New Function │
├─────────────────────────────────────┤
│ Name: getKnowiRecords │
│ Description: Get Knowi DB records │
│ Service: knowi │
│ Implementation Type: From File │
│ │
│ Function File Path: │
│ services/knowi/knowiDb.js │
│ │
│ Export Name: │
│ getPaginatedKnowiRecords │
│ │
│ Parameters: [Add Parameter] │
│ - paramName: page │
│ paramType: number │
│ required: □ │
│ defaultValue: 1 │
│ │
│ - paramName: limit │
│ paramType: number │
│ required: □ │
│ defaultValue: 10 │
│ │
│ Response Type: JSON │
│ Active: ✓ │
│ │
│ [Cancel] [Save] │
└─────────────────────────────────────┘
Example: Create a Developer Key
┌─────────────────────────────────────┐
│ Create New Key │
├─────────────────────────────────────┤
│ Name: Developer Key - John │
│ Key Value: dev_abc123xyz │
│ Key Type: developer │
│ Description: Key for John's apps │
│ │
│ Allowed Routes: (select multiple) │
│ ☑ GET /api/hello │
│ ☑ GET /api/knowi │
│ □ POST /api/gptChat │
│ │
│ Rate Limit: │
│ - Window: 900000 (15 min) │
│ - Max Requests: 100 │
│ │
│ Active: ✓ │
│ │
│ [Cancel] [Save] │
└─────────────────────────────────────┘
Function (Inline Code):
try {
const { collection, limit = 10 } = req.query
const results = await payload.find({
collection: collection,
limit: parseInt(limit),
})
res.json({
success: true,
data: results.docs,
total: results.totalDocs,
})
} catch (error) {
res.status(500).json({
success: false,
error: error.message,
})
}Route:
- Path:
/api/query - Method:
GET - Requires Auth: ✓
Usage:
curl "http://localhost:3000/api/query?collection=users&limit=5&key=your-api-key"Function (Inline Code):
try {
const { collection, data } = req.body
if (!collection || !data) {
return res.status(400).json({
error: 'collection and data are required',
})
}
const result = await payload.create({
collection: collection,
data: data,
})
res.json({
success: true,
id: result.id,
data: result,
})
} catch (error) {
res.status(500).json({
success: false,
error: error.message,
})
}Route:
- Path:
/api/create - Method:
POST - Requires Auth: ✓
Usage:
curl -X POST http://localhost:3000/api/create \
-H "x-api-key: your-key" \
-H "Content-Type: application/json" \
-d '{
"collection": "media",
"data": {
"alt": "My image"
}
}'First, copy your es6Api GPT function:
# Copy es6Api services
cp -r /Users/mroberts/Development/dev_JavaScript/personal/es6Api/src/services \
/Users/mroberts/Development/dev_JavaScript/personal/cctest/api-command/src/Create Function in Admin:
Name: gptChat
Service: gpt
Implementation Type: From File
Function File Path: services/gpt/gptChat.js
Export Name: gptChat
Response Type: stream
Timeout: 60000
Create Route:
Name: GPT Chat
Path: /api/gpt
Method: POST
Function: gptChat (dropdown)
Requires Auth: ✓
Allowed Keys: [select admin keys]
When you select Inline Code and choose a service, a template is auto-populated!
// Knowi database query example
const page = parseInt(req.query.page) || 1
const limit = parseInt(req.query.limit) || 10
try {
// Your Knowi query logic here
const results = await payload.find({
collection: 'your-collection',
page,
limit
})
res.json({
data: results.docs,
page: results.page,
totalPages: results.totalPages
})
} catch (error) {
res.status(500).json({ error: error.message })
}// FileMaker operation example
try {
const data = req.body
// Your FileMaker logic here
// Example: Call FileMaker Data API
res.json({
status: 'success',
message: 'Operation completed'
})
} catch (error) {
res.status(500).json({ error: error.message })
}// Custom function example
try {
// Access request data
const params = req.query
const body = req.body
// Access Payload CMS
const data = await payload.find({
collection: 'your-collection'
})
// Send response
res.json({
status: 'success',
data: data.docs
})
} catch (error) {
res.status(500).json({ error: error.message })
}Step 1: Create Function
- Functions → Create New
- Name:
getWeather - Service:
other - Implementation Type:
Inline Code - Code:
try { const { city } = req.query // Call external weather API const response = await fetch( `https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${city}` ) const data = await response.json() res.json({ city: city, temp: data.current.temp_c, condition: data.current.condition.text }) } catch (error) { res.status(500).json({ error: error.message }) }
- Save
Step 2: Create Route
- Routes → Create New
- Name:
Weather Endpoint - Path:
/api/weather - Method:
GET - Function: Select
getWeather - Requires Auth: Check ✓
- Save
Step 3: Create API Key (if needed)
- Keys → Create New
- Name:
Weather App Key - Key Value:
weather_abc123 - Key Type:
standard - Allowed Routes: Check
GET /api/weather - Save
Step 4: Test
curl "http://localhost:3000/api/weather?city=London&key=weather_abc123"✅ Done! No server restart needed.
When writing inline code, you have access to:
req.query // URL query parameters
req.body // POST/PUT body data
req.params // URL parameters
req.headers // Request headers
req.method // HTTP method
req.path // Request pathres.json({ data }) // Send JSON response
res.status(404).json({}) // Set status code
res.send('text') // Send text
res.redirect('/path') // Redirectawait payload.find({ collection: 'users' })
await payload.findByID({ collection: 'users', id: '123' })
await payload.create({ collection: 'users', data: {} })
await payload.update({ collection: 'users', id: '123', data: {} })
await payload.delete({ collection: 'users', id: '123' })console.log() // Logging
JSON // JSON utilities
Math // Math operations
Date // Date handling
fetch() // HTTP requests (if available)- Navigate to Keys collection
- Click on any key
- View:
- Last Used At
- Usage Count
- Associated User
- Navigate to Routes collection
- Find the route
- Uncheck Active
- Save
The route is immediately unavailable!
- Navigate to Functions collection
- Find your function
- Edit the Function Code field
- Save
Changes take effect immediately (cache is cleared automatically).
- ✅ Always validate input
- ✅ Use try/catch blocks
- ✅ Check for required parameters
- ✅ Sanitize user data
- ❌ Don't expose sensitive env vars directly
- ❌ Don't execute user-provided code
- ✅ Use descriptive names
- ✅ Set expiration dates
- ✅ Limit routes per key
- ✅ Set appropriate rate limits
- ✅ Use different key types for different access levels
- ✅ Always require auth for sensitive operations
- ✅ Use appropriate HTTP methods (GET for read, POST for create, etc.)
- ✅ Set specific allowed keys for restricted routes
- Check function code for syntax errors
- Check console logs for detailed error
- Ensure all required variables are defined
- Add
?key=your-keyto URL or - Add
x-api-key: your-keyheader
- Check route is Active
- Verify path matches exactly
- Check method (GET/POST/etc) matches
- Hard refresh admin page (Cmd+Shift+R)
- Check function is marked Active
- Check route is marked Active
To migrate all your existing es6Api functions at once:
# 1. Copy all source files
cp -r /Users/mroberts/Development/dev_JavaScript/personal/es6Api/src/services \
/Users/mroberts/Development/dev_JavaScript/personal/cctest/api-command/src/
cp -r /Users/mroberts/Development/dev_JavaScript/personal/es6Api/src/routes \
/Users/mroberts/Development/dev_JavaScript/personal/cctest/api-command/src/
# 2. Run the seed script
pnpm tsx src/scripts/seedApiData.tsThis will create:
- ✅ All functions from es6Api
- ✅ All routes
- ✅ API keys
- ✅ Endpoints
Then manage everything via admin UI!
- Copy your es6Api services to this project
- Create functions via admin for each service
- Create routes linking to functions
- Create API keys for different access levels
- Test endpoints with curl or Postman
- Monitor usage through Keys collection
Everything is now manageable through the UI! 🎉
No code changes needed - just point and click in the admin panel.