• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
mattspieg

mattspieg

sg-luma-events

Public
Like
sg-luma-events
Home
Code
9
RAILWAY_MIGRATION.md
README.md
deno-server.ts
deploy-to-railway.md
H
luma-proxy.ts
main.tsx
node-server.js
package.json
railway.toml
Branches
1
Pull requests
Remixes
History
Environment variables
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
RAILWAY_MIGRATION.md
Code
/
RAILWAY_MIGRATION.md
Search
6/3/2025
Viewing readonly version of main branch: v24
View latest version
RAILWAY_MIGRATION.md

πŸš‚ Railway Migration Guide

Your Luma CORS proxy is ready for Railway! Here are two deployment options:

🎯 Option 1: Deno Deployment (Recommended)

Step 1: Create server.ts

import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; // Your existing proxy logic from luma-proxy.ts async function handleRequest(req: Request): Promise<Response> { // Handle preflight OPTIONS request if (req.method === 'OPTIONS') { return new Response(null, { status: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', 'Access-Control-Max-Age': '86400', }, }); } // Only allow GET requests if (req.method !== 'GET') { return new Response('Method not allowed', { status: 405, headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'text/plain', }, }); } try { // Extract query parameters from the incoming request const url = new URL(req.url); const searchParams = url.searchParams; // Check if we should filter to next 3 events const limitNext3 = searchParams.get('next3') === 'true'; // Build the Luma API URL with query parameters const lumaUrl = new URL('https://api.lu.ma/public/v1/calendar/list-events'); // If we're filtering for next 3, optimize the API call if (limitNext3) { // Remove next3 from params to forward to Luma API searchParams.delete('next3'); // Use correct Luma API parameter names from the documentation lumaUrl.searchParams.set('pagination_limit', '100'); // Try to get events from a reasonable time range // Use 'after' parameter with current date to get future events const today = new Date(); const todayISO = today.toISOString(); lumaUrl.searchParams.set('after', todayISO); // Sort by start date ascending to get earliest upcoming events first lumaUrl.searchParams.set('sort_column', 'start_at'); lumaUrl.searchParams.set('sort_direction', 'asc'); } // Forward all remaining query parameters to the Luma API for (const [key, value] of searchParams.entries()) { lumaUrl.searchParams.set(key, value); } // Get API key from environment variable (more secure) or fallback to hardcoded const apiKey = Deno.env.get('LUMA_API_KEY') || 'secret-FMPoZlwNgVJ0qkCSn2EIHpTUA'; // Make the request to Luma API with the API key const lumaResponse = await fetch(lumaUrl.toString(), { method: 'GET', headers: { 'x-luma-api-key': apiKey, 'Content-Type': 'application/json', }, }); // Get the response data const data = await lumaResponse.json(); let filteredData = data; if (limitNext3 && data.entries && Array.isArray(data.entries)) { const now = new Date(); // First, try to find upcoming events let upcomingEvents = data.entries .filter(entry => { const eventStartTime = new Date(entry.event.start_at); return eventStartTime > now; // Only future events }) .sort((a, b) => { const dateA = new Date(a.event.start_at); const dateB = new Date(b.event.start_at); return dateA - dateB; // Sort by date ascending (earliest first) }); // If we have upcoming events, use them if (upcomingEvents.length > 0) { upcomingEvents = upcomingEvents.slice(0, 3); filteredData = { ...data, entries: upcomingEvents, has_more: upcomingEvents.length === 3 && (data.entries.length > upcomingEvents.length || data.has_more) }; } else { // No upcoming events, fall back to most recent past events const recentPastEvents = data.entries .filter(entry => { const eventStartTime = new Date(entry.event.start_at); return eventStartTime <= now; // Only past events }) .sort((a, b) => { const dateA = new Date(a.event.start_at); const dateB = new Date(b.event.start_at); return dateB - dateA; // Sort by date descending (most recent first) }) .slice(0, 3); filteredData = { ...data, entries: recentPastEvents, has_more: false, // Since we're showing past events as fallback _fallback: 'recent_past_events' // Indicator that we fell back to past events }; } } // Return the data with CORS headers return new Response(JSON.stringify(filteredData), { status: lumaResponse.status, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', 'Content-Type': 'application/json', }, }); } catch (error) { console.error('Proxy error:', error); return new Response(JSON.stringify({ error: 'Failed to fetch from Luma API', message: error instanceof Error ? error.message : 'Unknown error' }), { status: 500, headers: { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'application/json', }, }); } } // Start the server const port = parseInt(Deno.env.get("PORT") || "8000"); console.log(`πŸš€ Luma CORS Proxy running on port ${port}`); serve(handleRequest, { port });

Step 2: Create railway.toml

[build] builder = "nixpacks" [deploy] startCommand = "deno run --allow-net --allow-env server.ts"

🎯 Option 2: Node.js Deployment

Step 1: Create package.json

{ "name": "luma-cors-proxy", "version": "1.0.0", "description": "CORS proxy for Luma API", "main": "server.js", "scripts": { "start": "node server.js", "dev": "node server.js" }, "dependencies": { "express": "^4.18.2" }, "engines": { "node": ">=18" } }

Step 2: Create server.js

const express = require('express'); const app = express(); // Your proxy logic converted to Express app.use((req, res, next) => { // Handle CORS res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.header('Access-Control-Max-Age', '86400'); return res.status(200).end(); } next(); }); app.get('*', async (req, res) => { try { // Your existing proxy logic here, but using: // - process.env.LUMA_API_KEY instead of Deno.env.get() // - require('node-fetch') or built-in fetch for HTTP requests const apiKey = process.env.LUMA_API_KEY || 'secret-FMPoZlwNgVJ0qkCSn2EIHpTUA'; // ... rest of your proxy logic } catch (error) { console.error('Proxy error:', error); res.status(500).json({ error: 'Failed to fetch from Luma API', message: error.message }); } }); const port = process.env.PORT || 8000; app.listen(port, () => { console.log(`πŸš€ Luma CORS Proxy running on port ${port}`); });

πŸš€ Railway Deployment Steps

  1. Create a new GitHub repository with your chosen option files
  2. Go to railway.app and sign up/login
  3. Click "Deploy from GitHub repo"
  4. Select your repository
  5. Set environment variables (optional):
    • LUMA_API_KEY = secret-FMPoZlwNgVJ0qkCSn2EIHpTUA
  6. Deploy! Railway will auto-detect and deploy

πŸ”— After Deployment

Your proxy will be available at:

https://[your-app-name].up.railway.app

Update your Webflow code to use this new URL instead of the Val Town URL.

βœ… Benefits of Railway

  • βœ… Free tier available
  • βœ… Auto-scaling
  • βœ… Custom domains
  • βœ… Environment variables
  • βœ… Git-based deployments
  • βœ… Built-in monitoring
  • βœ… Your client already uses it!

πŸ†˜ Need Help?

If you run into any issues:

  1. Check Railway logs in the dashboard
  2. Verify environment variables are set
  3. Test the proxy URL directly in browser
  4. Compare with your working Val Town version

Your migration should be seamless since it's just a simple stateless proxy! πŸŽ‰

FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
Β© 2026 Val Town, Inc.