You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@burr.apache.org by sk...@apache.org on 2026/02/06 21:44:48 UTC
(burr) 01/05: feat: deploy Burr in Vercel
This is an automated email from the ASF dual-hosted git repository.
skrawcz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/burr.git
commit 00901a6075a48d3b6aa4faba7ff40e572b945a8a
Author: echonesis <ec...@gmail.com>
AuthorDate: Mon Dec 1 14:31:58 2025 +0800
feat: deploy Burr in Vercel
---
examples/deployment/vercel/.gitignore | 2 +
examples/deployment/vercel/README.md | 123 ++++++++++++++++++++++++++
examples/deployment/vercel/api/counter.py | 111 +++++++++++++++++++++++
examples/deployment/vercel/app/__init__.py | 0
examples/deployment/vercel/app/counter_app.py | 43 +++++++++
examples/deployment/vercel/requirements.txt | 1 +
6 files changed, 280 insertions(+)
diff --git a/examples/deployment/vercel/.gitignore b/examples/deployment/vercel/.gitignore
new file mode 100644
index 00000000..c8a73361
--- /dev/null
+++ b/examples/deployment/vercel/.gitignore
@@ -0,0 +1,2 @@
+.vercel
+.env*.local
diff --git a/examples/deployment/vercel/README.md b/examples/deployment/vercel/README.md
new file mode 100644
index 00000000..8b95a152
--- /dev/null
+++ b/examples/deployment/vercel/README.md
@@ -0,0 +1,123 @@
+# Deploy Burr in Vercel
+
+[Vercel](https://vercel.com/) - serverless platform for frontend frameworks and serverless functions.
+
+Here we have an example of how to deploy a Burr application as a Vercel Serverless Function.
+
+## Prerequisites
+
+- **Node.js**: Required for Vercel CLI (v14 or higher)
+- **Vercel Account**: Sign up at [vercel.com](https://vercel.com/signup) (free tier available)
+
+## Step-by-Step Guide
+
+### 1. Install Vercel CLI:
+
+```bash
+npm install -g vercel
+```
+
+### 2. Local tests:
+
+Start the local development server:
+
+```bash
+vercel dev
+```
+
+Send test request to check if the function executes correctly:
+
+```bash
+curl -X POST "http://localhost:3000/api/counter" \
+ -H "Content-Type: application/json" \
+ -d '{"number": 5}'
+```
+
+Expected response:
+
+```json
+{"counter": 5, "counter_limit": 5, "__SEQUENCE_ID": 5, "__PRIOR_STEP": "result"}
+```
+
+### 3. Login to Vercel:
+
+```bash
+vercel login
+```
+
+This will open your browser to authenticate with your Vercel account.
+
+### 4. Deploy to Vercel (Preview):
+
+Deploy to a preview environment for testing:
+
+```bash
+vercel
+```
+
+### 5. Test Preview Deployment:
+
+Vercel will provide a preview URL. Test it:
+
+```bash
+curl -X POST "https://your-project-xxx.vercel.app/api/counter" \
+ -H "Content-Type: application/json" \
+ -d '{"number": 5}'
+```
+
+### 6. Deploy to Production:
+
+Once preview testing is successful, deploy to production:
+
+```bash
+vercel --prod
+```
+
+Your production URL will be:
+
+```
+https://your-project.vercel.app
+```
+
+### 7. Test Production Deployment:
+
+```bash
+curl -X POST "https://your-project.vercel.app/api/counter" \
+ -H "Content-Type: application/json" \
+ -d '{"number": 5}'
+```
+
+## Alternative: Deploy via Git Integration (Recommended)
+
+### Import project in Vercel Dashboard:
+
+- Go to https://vercel.com/new
+- Click "Import Git Repository"
+- Select your repository
+- Click "Deploy"
+
+## Troubleshooting
+
+### If deployment fails:
+
+View detailed logs:
+
+```bash
+vercel logs --follow
+```
+
+### If function returns 404:
+
+Ensure your handler file is in the `api/` directory with correct format.
+
+### If you see deployment URL instead of production URL:
+
+The production URL is always in the format: `https://your-project.vercel.app`
+
+Check your Vercel Dashboard → Domains section for the correct URL.
+
+## Resources
+
+- [Vercel Documentation](https://vercel.com/docs)
+- [Vercel Python Runtime](https://vercel.com/docs/functions/runtimes/python)
+- [Vercel CLI Reference](https://vercel.com/docs/cli)
\ No newline at end of file
diff --git a/examples/deployment/vercel/api/counter.py b/examples/deployment/vercel/api/counter.py
new file mode 100644
index 00000000..8a8725ef
--- /dev/null
+++ b/examples/deployment/vercel/api/counter.py
@@ -0,0 +1,111 @@
+"""
+Vercel Serverless Function for counter application
+Endpoint: /api/counter
+"""
+from http.server import BaseHTTPRequestHandler
+import json
+from app import counter_app
+
+
+class handler(BaseHTTPRequestHandler):
+ """Vercel Serverless Function handler.
+
+ This class handles HTTP requests for the counter API endpoint.
+ Must inherit from BaseHTTPRequestHandler to work with Vercel's Python runtime.
+ """
+
+ def do_POST(self):
+ """Handle POST requests to increment counter.
+
+ Expects JSON body with 'number' field indicating count limit.
+ Returns serialized application state on success.
+ """
+ try:
+ # Read request body
+ content_length = int(self.headers.get('Content-Length', 0))
+ body = self.rfile.read(content_length)
+
+ # Parse JSON payload
+ data = json.loads(body.decode('utf-8'))
+
+ # Extract parameter (equivalent to Lambda's event["body"]["number"])
+ count_up_to = int(data.get("number", 0))
+
+ # Validate input
+ if count_up_to <= 0:
+ self.send_error_response(400, "number must be greater than 0")
+ return
+
+ # Execute business logic (identical to Lambda implementation)
+ app = counter_app.application(count_up_to)
+ action, result, state = app.run(halt_after=["result"])
+
+ # Return success response with serialized state
+ self.send_json_response(200, state.serialize())
+
+ except json.JSONDecodeError:
+ self.send_error_response(400, "Invalid JSON format")
+ except ValueError as e:
+ self.send_error_response(400, f"Invalid number format: {str(e)}")
+ except KeyError as e:
+ self.send_error_response(400, f"Missing required field: {str(e)}")
+ except Exception as e:
+ # Log error for debugging
+ print(f"Error in counter handler: {str(e)}")
+ import traceback
+ traceback.print_exc()
+ self.send_error_response(500, "Internal server error")
+
+ def do_GET(self):
+ """Handle GET requests - not allowed.
+
+ Returns 405 Method Not Allowed error.
+ """
+ self.send_error_response(405, "Only POST method is allowed")
+
+ def do_PUT(self):
+ """Handle PUT requests - not allowed.
+
+ Returns 405 Method Not Allowed error.
+ """
+ self.send_error_response(405, "Only POST method is allowed")
+
+ def do_DELETE(self):
+ """Handle DELETE requests - not allowed.
+
+ Returns 405 Method Not Allowed error.
+ """
+ self.send_error_response(405, "Only POST method is allowed")
+
+ def send_json_response(self, status_code, data):
+ """Send JSON response to client.
+
+ Args:
+ status_code: HTTP status code
+ data: Response data (dict, list, or any JSON-serializable object)
+ """
+ self.send_response(status_code)
+ self.send_header('Content-Type', 'application/json')
+ self.end_headers()
+
+ if isinstance(data, (dict, list)):
+ response_body = json.dumps(data, ensure_ascii=False)
+ else:
+ response_body = str(data)
+
+ self.wfile.write(response_body.encode('utf-8'))
+
+ def send_error_response(self, status_code, message):
+ """Send error response to client.
+
+ Args:
+ status_code: HTTP error status code
+ message: Error message string to include in response body
+ """
+ self.send_response(status_code)
+ self.send_header('Content-Type', 'application/json')
+ self.end_headers()
+
+ error_body = json.dumps({'error': message})
+ self.wfile.write(error_body.encode('utf-8'))
+
\ No newline at end of file
diff --git a/examples/deployment/vercel/app/__init__.py b/examples/deployment/vercel/app/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/deployment/vercel/app/counter_app.py b/examples/deployment/vercel/app/counter_app.py
new file mode 100644
index 00000000..76eafabe
--- /dev/null
+++ b/examples/deployment/vercel/app/counter_app.py
@@ -0,0 +1,43 @@
+"""
+This is a very simple counting application.
+
+It's here to help you get the mechanics of deploying a Burr application to AWS Lambda.
+"""
+
+import time
+
+import burr.core
+from burr.core import Application, Result, State, default, expr
+from burr.core.action import action
+from burr.core.graph import GraphBuilder
+
+
+@action(reads=["counter"], writes=["counter"])
+def counter(state: State) -> State:
+ result = {"counter": state["counter"] + 1}
+ time.sleep(0.5) # sleep to simulate a longer running function
+ return state.update(**result)
+
+
+# our graph.
+graph = (
+ GraphBuilder()
+ .with_actions(counter=counter, result=Result("counter"))
+ .with_transitions(
+ ("counter", "counter", expr("counter < counter_limit")),
+ ("counter", "result", default),
+ )
+ .build()
+)
+
+
+def application(count_up_to: int = 10) -> Application:
+ """function to return a burr application"""
+ return (
+ burr.core.ApplicationBuilder()
+ .with_graph(graph)
+ .with_state(**{"counter": 0, "counter_limit": count_up_to})
+ .with_entrypoint("counter")
+ .build()
+ )
+
\ No newline at end of file
diff --git a/examples/deployment/vercel/requirements.txt b/examples/deployment/vercel/requirements.txt
new file mode 100644
index 00000000..a78cac9d
--- /dev/null
+++ b/examples/deployment/vercel/requirements.txt
@@ -0,0 +1 @@
+burr