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 <[email protected]>
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

Reply via email to