Skip to content

Commit

Permalink
Ready for Cloud
Browse files Browse the repository at this point in the history
  • Loading branch information
balegas committed Nov 18, 2024
1 parent 4890bff commit dc9da79
Show file tree
Hide file tree
Showing 5 changed files with 3,222 additions and 3,476 deletions.
6 changes: 1 addition & 5 deletions examples/nextjs-ssr-example/app/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import pgPkg from "pg"
const { Client } = pgPkg

const db = new Client({
host: `localhost`,
port: 54321,
password: `password`,
user: `postgres`,
database: `electric`,
connectionString: process.env.DATABASE_URL,
})

db.connect()
Expand Down
23 changes: 21 additions & 2 deletions examples/nextjs-ssr-example/app/shape-proxy/[...table]/route.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
export async function GET(request: Request) {
const url = new URL(request.url)
const originUrl = new URL(`http://localhost:3000/v1/shape`)
const originUrl = new URL(
process.env.ELECTRIC_URL
? `${process.env.ELECTRIC_URL}/v1/shape`
: `http://localhost:3000/v1/shape`
)

url.searchParams.forEach((value, key) => {
originUrl.searchParams.set(key, value)
})

if (process.env.DATABASE_ID) {
originUrl.searchParams.set(`database_id`, process.env.DATABASE_ID)
}

const headers = new Headers()
if (process.env.ELECTRIC_TOKEN) {
originUrl.searchParams.set(`token`, process.env.ELECTRIC_TOKEN)
}

const newRequest = new Request(originUrl.toString(), {
method: `GET`,
headers,
})

// When proxying long-polling requests, content-encoding & content-length are added
// erroneously (saying the body is gzipped when it's not) so we'll just remove
// them to avoid content decoding errors in the browser.
//
// Similar-ish problem to https://github.com/wintercg/fetch/issues/23
let resp = await fetch(originUrl.toString())
let resp = await fetch(newRequest)
if (resp.headers.get(`content-encoding`)) {
const headers = new Headers(resp.headers)
headers.delete(`content-encoding`)
Expand Down
7 changes: 4 additions & 3 deletions examples/nextjs-ssr-example/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "@electric-examples/basic-example",
"name": "@electric-examples/nextjs-ssr-example",
"private": true,
"version": "0.0.1",
"author": "ElectricSQL",
"license": "Apache-2.0",
"type": "module",
"scripts": {
"backend:up": "PROJECT_NAME=nextjs-basic-example pnpm -C ../../ run example-backend:up && pnpm db:migrate",
"backend:down": "PROJECT_NAME=nextjs-basic-example pnpm -C ../../ run example-backend:down",
"backend:up": "PROJECT_NAME=nextjs-ssr-example pnpm -C ../../ run example-backend:up && pnpm db:migrate",
"backend:down": "PROJECT_NAME=nextjs-ssr-example pnpm -C ../../ run example-backend:down",
"db:migrate": "dotenv -e ../../.env.dev -- pnpm exec pg-migrations apply --directory ./db/migrations",
"dev": "next dev --turbo -p 5173",
"build": "next build --experimental-build-mode=compile",
Expand Down Expand Up @@ -37,6 +37,7 @@
"dotenv": "^16.4.5",
"eslint": "^8.57.0",
"typescript": "^5.5.3",
"sst": "3.3.7",
"vite": "^5.3.4"
}
}
115 changes: 115 additions & 0 deletions examples/nextjs-ssr-example/sst.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="./.sst/platform/config.d.ts" />

import { execSync } from "child_process"

export default $config({
app(input) {
return {
name: `nextjs-ssr-example`,
removal: input?.stage === `production` ? `retain` : `remove`,
home: `aws`,
providers: {
cloudflare: `5.42.0`,
aws: {
version: `6.57.0`,
},
neon: `0.6.3`,
},
}
},
async run() {
const project = neon.getProjectOutput({ id: process.env.NEON_PROJECT_ID! })
const base = {
projectId: project.id,
branchId: project.defaultBranchId,
}

const db = new neon.Database(`nextjs-ssr-example`, {
...base,
ownerName: `neondb_owner`,
})

const databaseUri = getNeonDbUri(project, db)
try {
databaseUri.apply(applyMigrations)

const electricInfo = databaseUri.apply((uri) =>
addDatabaseToElectric(uri)
)

const website = deployNextJsExample(electricInfo, databaseUri)
return {
databaseUri,
database_id: electricInfo.id,
electric_token: electricInfo.token,
website: website.url,
}
} catch (e) {
console.error(`Failed to deploy nextjs ssr example stack`, e)
}
},
})

function applyMigrations(uri: string) {
execSync(`pnpm exec pg-migrations apply --directory ./db/migrations`, {
env: {
...process.env,
DATABASE_URL: uri,
},
})
}

function deployNextJsExample(
electricInfo: $util.Output<{ id: string; token: string }>,
uri: $util.Output<string>
) {
return new sst.aws.Nextjs(`nextjs`, {
environment: {
ELECTRIC_URL: process.env.ELECTRIC_API!,
ELECTRIC_TOKEN: electricInfo.token,
DATABASE_ID: electricInfo.id,
DATABASE_URL: uri,
},
domain: {
name: `nextjs${$app.stage === `production` ? `` : `-stage-${$app.stage}`}.electric-sql.com`,
dns: sst.cloudflare.dns(),
},
})
}

function getNeonDbUri(
project: $util.Output<neon.GetProjectResult>,
db: neon.Database
) {
const passwordOutput = neon.getBranchRolePasswordOutput({
projectId: project.id,
branchId: project.defaultBranchId,
roleName: db.ownerName,
})

return $interpolate`postgresql://${passwordOutput.roleName}:${passwordOutput.password}@${project.databaseHost}/${db.name}?sslmode=require`
}

async function addDatabaseToElectric(
uri: string
): Promise<{ id: string; token: string }> {
const adminApi = process.env.ELECTRIC_ADMIN_API

const result = await fetch(`${adminApi}/v1/databases`, {
method: `PUT`,
headers: { "Content-Type": `application/json` },
body: JSON.stringify({
database_url: uri,
region: `us-east-1`,
}),
})

if (!result.ok) {
throw new Error(
`Could not add database to Electric (${result.status}): ${await result.text()}`
)
}

return await result.json()
}
Loading

0 comments on commit dc9da79

Please sign in to comment.