Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions packages/quickstart/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
`eslint:recommended`,
`plugin:@typescript-eslint/recommended`,
`plugin:prettier/recommended`,
],
parserOptions: {
ecmaVersion: 2022,
requireConfigFile: false,
sourceType: `module`,
ecmaFeatures: {
jsx: true,
},
},
parser: `@typescript-eslint/parser`,
plugins: [`prettier`],
rules: {
quotes: [`error`, `backtick`],
'no-unused-vars': `off`,
'@typescript-eslint/no-unused-vars': [
`error`,
{
argsIgnorePattern: `^_`,
varsIgnorePattern: `^_`,
caughtErrorsIgnorePattern: `^_`,
},
],
},
ignorePatterns: [
'**/node_modules/**',
'**/dist/**',
'tsup.config.ts',
'vitest.config.ts',
'.eslintrc.js',
],
}
6 changes: 6 additions & 0 deletions packages/quickstart/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"semi": false,
"tabWidth": 2,
"singleQuote": true
}
65 changes: 65 additions & 0 deletions packages/quickstart/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# @electric-sql/quickstart

CLI package for the [ElectricSQL Quickstart](https://electric-sql.com/docs/quickstart).

## Usage

Create a new app using [Electric](https://electric-sql.com/product/electric) with [TanStack DB](https://tanstack.com/db), based on the [examples/tanstack-db-web-starter](https://github.com/electric-sql/electric/tree/main/examples/tanstack-db-web-starter) [TanStack Start](http://tanstack.com/start) template app:

```bash
pnpx @electric-sql/quickstart my-electric-app
```

This command will:

1. pull in the template app using gitpick
2. provision cloud resources
- a Postgres database using Neon
- an Electric sync service using Electric Cloud
- fetch their access credentials
3. configure the local `.env` to use the credentials
4. add `psql`, `claim` and `deploy` commands to the package.json
- also using the generated credentials

## Environment Variables

The CLI automatically generates these environment variables:

- `DATABASE_URL` - PostgreSQL connection string
- `ELECTRIC_SECRET` - Electric Cloud authentication secret
- `ELECTRIC_SOURCE_ID` - Electric sync service identifier

## Commands

```bash
pnpm dev # Start development server
pnpm psql # Connect to PostgreSQL database
pnpm claim # Claim temporary resources
pnpm deploy # Deploy to Netlify
```

### `pnpm psql`

Connect directly to your PostgreSQL database using the configured `DATABASE_URL`:

### `pnpm claim`

Claim temporary resources to move them to your permanent Electric Cloud and Neon accounts.

### `pnpm deploy`

Deploy your app to Netlify with all environment variables configured.

## Development

This package is part of the Electric monorepo. To work on it:

```bash
# From the monorepo root
pnpm install # Install all workspace dependencies
pnpm build # Build all packages

# From packages/quickstart
pnpm build # Compile TypeScript
pnpm dev # Build and test locally
```
71 changes: 71 additions & 0 deletions packages/quickstart/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "@electric-sql/quickstart",
"version": "1.0.0",
"description": "CLI package for the ElectricSQL Quickstart.",
"type": "module",
"main": "./dist/index.js",
"bin": {
"quickstart": "./dist/cli.js"
},
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
},
"scripts": {
"build": "shx rm -rf dist && tsup",
"prepack": "pnpm build",
"dev": "pnpm run build && node dist/cli.js",
"format": "eslint . --fix",
"stylecheck": "eslint . --quiet",
"test": "pnpm exec vitest",
"coverage": "pnpm exec vitest --coverage",
"typecheck": "tsc -p tsconfig.json"
},
"dependencies": {
"node-fetch": "^2.7.0"
},
"devDependencies": {
"@types/node": "^20.10.0",
"@types/node-fetch": "^2.6.11",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
"@vitest/coverage-istanbul": "2.1.4",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"netlify-cli": "^23.5.1",
"prettier": "^3.3.2",
"shx": "^0.3.4",
"tsup": "^8.0.1",
"typescript": "^5.5.2",
"vitest": "^3.0.0"
},
"files": [
"dist",
"src"
],
"keywords": [
"cli",
"db",
"electric",
"electric-sql",
"quickstart",
"starter",
"tanstack"
],
"author": "ElectricSQL team and contributors.",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/electric-sql/electric.git"
},
"bugs": {
"url": "https://github.com/electric-sql/electric/issues"
},
"homepage": "https://electric-sql.com"
}
65 changes: 65 additions & 0 deletions packages/quickstart/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env node

import { provisionElectricResources } from './electric-api.js'
import { setupTemplate } from './template-setup.js'

async function main() {
const args = process.argv.slice(2)

if (args.length === 0) {
console.error(`Usage: quickstart <app-name>`)

process.exit(1)
}

const appName = args[0]

// Validate app name
if (!/^[a-zA-Z0-9-_]+$/.test(appName)) {
console.error(
`App name must contain only letters, numbers, hyphens, and underscores`
)

process.exit(1)
}

console.log(`Creating app: ${appName}`)

try {
console.log(`Provisioning resources...`)
const credentials = await provisionElectricResources()

// Step 2: Setup TanStack Start template
console.log(`Setting up template...`)
await setupTemplate(appName, credentials)

// Step 3: Display completion message
console.log(`Setup complete`)
console.log(``)
console.log(`Next steps:`)
console.log(` cd ${appName}`)
console.log(` pnpm dev`)
console.log(``)
console.log(`Commands:`)
console.log(` pnpm psql # Connect to database`)
console.log(` pnpm claim # Claim resources`)
console.log(` pnpm deploy # Deploy to Netlify`)
console.log(``)
console.log(`Tutorial: https://electric-sql.com/docs`)
} catch (error) {
console.error(
`Setup failed:`,
error instanceof Error ? error.message : error
)
process.exit(1)
}
}

if (import.meta.url === `file://${process.argv[1]}`) {
main().catch((error) => {
console.error(`Unexpected error:`, error)
process.exit(1)
})
}

export { main }
45 changes: 45 additions & 0 deletions packages/quickstart/src/commands/claim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { claimResources } from '../electric-api'

export async function claimCommand(): Promise<void> {
const sourceId = process.env.ELECTRIC_SOURCE_ID
const secret = process.env.ELECTRIC_SECRET

if (!sourceId || !secret) {
console.error(
`Missing ELECTRIC_SOURCE_ID or ELECTRIC_SECRET environment variables`
)
console.error(`Ensure .env file exists with Electric credentials`)
process.exit(1)
}

console.log(`Initiating resource claim...`)

try {
const result = await claimResources(sourceId, secret)

console.log(`Resource claim initiated`)
console.log(``)
console.log(`Open URL to complete claim:`)
console.log(result.claimUrl)
console.log(``)
console.log(`This will:`)
console.log(`- Link Electric Cloud account`)
console.log(`- Link Neon database account`)
console.log(`- Transfer temporary resources`)
console.log(``)
console.log(
`Warning: Temporary resources expire if not claimed within days`
)
} catch (error) {
console.error(
`Failed to initiate resource claim:`,
error instanceof Error ? error.message : error
)
console.error(``)
console.error(`Common issues:`)
console.error(`- Invalid credentials (check .env file)`)
console.error(`- Resources already claimed`)
console.error(`- Network connectivity`)
process.exit(1)
}
}
81 changes: 81 additions & 0 deletions packages/quickstart/src/commands/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { execSync } from 'child_process'

export function deployCommand(): void {
const requiredEnvVars = [
`ELECTRIC_SOURCE_ID`,
`ELECTRIC_SECRET`,
`DATABASE_URL`,
]
const missingVars = requiredEnvVars.filter((varName) => !process.env[varName])

if (missingVars.length > 0) {
console.error(
`Missing required environment variables:`,
missingVars.join(`, `)
)
console.error(`Ensure .env file contains all Electric credentials`)
process.exit(1)
}

console.log(`Starting deployment...`)
console.log(``)

try {
// Set Nitro preset for Netlify
process.env.NITRO_PRESET = `netlify`

console.log(`Building application...`)
execSync(`pnpm build`, {
stdio: `inherit`,
env: {
...process.env,
NITRO_PRESET: `netlify`,
},
})

console.log(`Deploying to Netlify...`)

// Deploy with environment variables
const deployCommand = [
`netlify deploy`,
`--prod`,
`--dir=dist`,
`--functions=.netlify/functions-internal`,
].join(` `)

execSync(deployCommand, {
stdio: `inherit`,
env: {
...process.env,
// Ensure all Electric credentials are available during deployment
ELECTRIC_SOURCE_ID: process.env.ELECTRIC_SOURCE_ID,
ELECTRIC_SECRET: process.env.ELECTRIC_SECRET,
DATABASE_URL: process.env.DATABASE_URL,
},
})

console.log(``)
console.log(`Deployment completed`)
console.log(``)
console.log(`App is live on Netlify with all environment variables.`)
} catch (error) {
console.error(
`Deployment failed:`,
error instanceof Error ? error.message : error
)
console.error(``)
console.error(`Common issues:`)
console.error(
`- Netlify CLI not installed (run: npm install -g netlify-cli)`
)
console.error(`- Not logged in (run: netlify login)`)
console.error(`- Build errors`)
console.error(`- Missing environment variables in Netlify`)
console.error(``)
console.error(`Required environment variables in Netlify settings:`)
console.error(`- ELECTRIC_SOURCE_ID`)
console.error(`- ELECTRIC_SECRET`)
console.error(`- DATABASE_URL`)
process.exit(1)
}
}
Loading
Loading