Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ jobs:

- name: Build all packages
run: pnpm build
env:
NEXT_PUBLIC_CONVEX_URL: https://terrific-llama-923.convex.cloud

- name: Upload build artifacts
uses: actions/upload-artifact@v4
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ jobs:

- name: Build
run: pnpm build
env:
NEXT_PUBLIC_CONVEX_URL: https://terrific-llama-923.convex.cloud

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ jobs:

- name: Build
run: pnpm build
env:
NEXT_PUBLIC_CONVEX_URL: https://terrific-llama-923.convex.cloud

release:
name: Create GitHub Release
Expand Down
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ node_modules/
.turbo/
coverage/
pnpm-lock.yaml
.pnpm-store/
.pnpm-store/
# Convex auto-generated files
packages/backend/convex/_generated/
packages/backend/convex/README.md
70 changes: 69 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [0.2.0] - 2026-06-29

### Overview

This release integrates **Convex** as the real-time backend for the Echo monorepo.
A new `@workspace/backend` package encapsulates all Convex schema definitions and
server-side functions, and both `apps/web` and `apps/widget` are wired to the live
Convex deployment via `ConvexProvider` and the generated TypeScript API.

---

### Added

#### `packages/backend` — New Convex workspace package

- Scaffolded `@workspace/backend` as a dedicated Convex workspace package
- `convex/schema.ts` — database schema with a `users` table (`name: string`) defined
using Convex's `defineSchema` and `defineTable` helpers
- `convex/users.ts` — two server functions:
- `getMany` — `query` that fetches all users from the database
- `add` — `mutation` that inserts a new user record (`name: "RISHII"`)
- `convex/_generated/` — committed auto-generated TypeScript API types (`api.d.ts`,
`api.js`, `dataModel.d.ts`, `server.d.ts`, `server.js`) for full type safety across
the monorepo without requiring a running Convex dev server in CI
- `package.json` — workspace package definition (`name: @workspace/backend`,
`convex: ^1.42.0` runtime dependency, `dev` and `setup` scripts)
- `tsconfig.json` — TypeScript configuration extending `@workspace/typescript-config`
- `.gitignore` — excludes `.env.local` (contains deployment secrets)
- `convex/README.md` — Convex-generated quickstart guide (excluded from Prettier)

#### Convex client integration in `apps/web` and `apps/widget`

- Added `@workspace/backend: workspace:*` and `convex: ^1.42.0` to both app
`package.json` files
- Added `@workspace/backend/*` TypeScript path alias in both `tsconfig.json` files,
pointing to `packages/backend/convex/*` for generated API type imports
- `components/theme-provider.tsx` — replaced static `NextThemesProvider` wrapper with
`ConvexProvider` backed by `ConvexReactClient`, reading from `NEXT_PUBLIC_CONVEX_URL`
environment variable
- `app/page.tsx` — replaced static math function demo with live Convex hooks:
- `useQuery(api.users.getMany)` — subscribes to real-time user list
- `useMutation(api.users.add)` — triggers user insertion via a button click

#### CI/tooling improvements

- Added `NEXT_PUBLIC_CONVEX_URL` environment variable to the `build` step in
`ci.yml`, `release.yml`, and `codeql.yml` — required for `ConvexReactClient`
to initialise with a valid URL during `next build` in CI environments
- Added `packages/backend/convex/_generated/` and `packages/backend/convex/README.md`
to `.prettierignore` — auto-generated files should not be subject to formatting rules

---

### Technical Decisions

- **`convex/_generated/` committed to source control** — Generated API types provide
full TypeScript safety across the monorepo without requiring every developer to run
`convex dev` before getting type completions. Files are excluded from Prettier.
- **`ConvexProvider` in theme-provider** — Centralises the Convex client provider at
the layout level so all pages and components have access to real-time hooks without
additional wrapper boilerplate.
- **`NEXT_PUBLIC_CONVEX_URL` as build-time env** — Convex's React client requires a
valid URL at module initialisation; adding it to CI workflows prevents static page
prerender failures in environments where `.env.local` is not present.

---

## [0.1.1] - 2026-06-28

### Fixed
Expand Down Expand Up @@ -182,6 +249,7 @@ Initial release of **Echo** — an enterprise-grade full-stack monorepo platform

---

[Unreleased]: https://github.com/RISHII7/echo/compare/v0.1.1...HEAD
[Unreleased]: https://github.com/RISHII7/echo/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/RISHII7/echo/compare/v0.1.1...v0.2.0
[0.1.1]: https://github.com/RISHII7/echo/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/RISHII7/echo/releases/tag/v0.1.0
62 changes: 51 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
[![Next.js](https://img.shields.io/badge/Next.js-16-black?logo=next.js)](https://nextjs.org)
[![Turborepo](https://img.shields.io/badge/Turborepo-2-EF4444?logo=turborepo)](https://turbo.build)
[![Tailwind CSS](https://img.shields.io/badge/Tailwind-4-38BDF8?logo=tailwindcss)](https://tailwindcss.com)
[![Convex](https://img.shields.io/badge/Convex-backend-EE342F?logo=convex)](https://convex.dev)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)

[Report Bug](https://github.com/RISHII7/echo/issues/new?template=bug_report.yml) · [Request Feature](https://github.com/RISHII7/echo/issues/new?template=feature_request.yml) · [Documentation](https://github.com/RISHII7/echo/wiki)
Expand Down Expand Up @@ -56,7 +57,8 @@ Echo is a production-ready, full-stack monorepo platform engineered for enterpri
## Features

- **Monorepo Architecture** — Turborepo with remote caching, task pipelines, and workspace dependency graph
- **Type Safety** — End-to-end TypeScript with strict mode across all packages
- **Real-Time Backend** — Convex reactive database with live queries and server mutations
- **Type Safety** — End-to-end TypeScript with strict mode and generated API types across all packages
- **Design System** — Shared UI component library built on shadcn/ui and Radix primitives
- **Performance** — Next.js Turbopack, React Server Components, and Tailwind CSS v4
- **Code Quality** — ESLint 9, Prettier, and TypeScript strict checks enforced on every commit
Expand All @@ -74,13 +76,14 @@ echo/
│ ├── web/ # Primary Next.js application
│ └── widget/ # Embeddable widget application
├── packages/
│ ├── backend/ # Convex real-time backend (schema + server functions)
│ ├── ui/ # Shared component library
│ ├── math/ # Shared utilities
│ ├── eslint-config/ # Shared ESLint configuration
│ └── typescript-config/ # Shared TypeScript configuration
```

The monorepo uses a **workspace dependency graph** where apps consume packages, and packages can depend on other packages. Turborepo ensures tasks run in topological order with caching at every layer.
The monorepo uses a **workspace dependency graph** where apps consume packages, and packages can depend on other packages. Turborepo ensures tasks run in topological order with caching at every layer. The `@workspace/backend` package provides a shared Convex client that both apps import for real-time data access.

---

Expand All @@ -93,6 +96,7 @@ The monorepo uses a **workspace dependency graph** where apps consume packages,
| UI Library | React 19 |
| Styling | Tailwind CSS 4 |
| Components | shadcn/ui + Radix UI |
| Backend | Convex (real-time DB) |
| Icons | Lucide React |
| Monorepo | Turborepo 2 |
| Package Manager | pnpm 10 |
Expand Down Expand Up @@ -129,9 +133,19 @@ pnpm dev

### Environment Variables

| Variable | Description | Required |
| --------------------- | --------------------------------- | -------- |
| `NEXT_PUBLIC_APP_URL` | Public URL of the web application | Yes |
Create `apps/web/.env.local` and `apps/widget/.env.local` with the following:

| Variable | Description | Required |
| ------------------------ | --------------------------------- | -------- |
| `NEXT_PUBLIC_CONVEX_URL` | Convex deployment URL | Yes |
| `NEXT_PUBLIC_APP_URL` | Public URL of the web application | No |

To get your `NEXT_PUBLIC_CONVEX_URL`, run `pnpm --filter backend dev` and copy the deployment URL from the Convex dashboard, or from `packages/backend/.env.local` after the first `convex dev` run.

```bash
# apps/web/.env.local
NEXT_PUBLIC_CONVEX_URL=https://<your-deployment>.convex.cloud
```

---

Expand All @@ -140,15 +154,16 @@ pnpm dev
### Commands

```bash
pnpm dev # Start all apps in development mode
pnpm build # Build all packages and apps
pnpm lint # Run linting across all workspaces
pnpm typecheck # Run type checking across all workspaces
pnpm format # Format all files with Prettier
pnpm dev # Start all apps in development mode (Next.js + Convex)
pnpm build # Build all packages and apps
pnpm lint # Run linting across all workspaces
pnpm typecheck # Run type checking across all workspaces
pnpm format # Format all files with Prettier

# Target a specific app
# Target a specific workspace
pnpm --filter web dev
pnpm --filter widget dev
pnpm --filter backend dev # Start Convex dev server only
```

### Branch Strategy
Expand Down Expand Up @@ -202,6 +217,11 @@ echo/
│ ├── web/ # Next.js web application
│ └── widget/ # Embeddable widget
├── packages/
│ ├── backend/ # Convex real-time backend
│ │ └── convex/
│ │ ├── _generated/ # Auto-generated TypeScript API types
│ │ ├── schema.ts # Database schema definitions
│ │ └── users.ts # User query & mutation functions
│ ├── ui/ # Shared component library
│ ├── math/ # Shared utilities
│ ├── eslint-config/ # ESLint config presets
Expand All @@ -217,6 +237,26 @@ echo/

## Packages

### `@workspace/backend`

Convex real-time backend package. Provides the database schema and server functions, with auto-generated TypeScript types for end-to-end type safety.

```tsx
import { api } from "@workspace/backend/_generated/api"
import { useQuery, useMutation } from "convex/react"

const users = useQuery(api.users.getMany)
const addUser = useMutation(api.users.add)
```

To start the Convex dev server:

```bash
pnpm --filter backend dev
# or from repo root:
pnpm dev # starts all apps including Convex
```

### `@workspace/ui`

Shared component library built on shadcn/ui and Radix UI primitives.
Expand Down
17 changes: 11 additions & 6 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
"use client"

import { useMutation, useQuery } from "convex/react"
import { api } from "@workspace/backend/_generated/api"
import { Button } from "@workspace/ui/components/button"
import { add } from "@workspace/math/add"

export default function Page() {
const users = useQuery(api.users.getMany)
const addUser = useMutation(api.users.add)
return (
<div className="flex min-h-svh items-center justify-center">
<div className="flex flex-col items-center justify-center gap-4">
<h1 className="text-2xl font-bold">Hello apps/web</h1>
<Button className="sm">Button</Button>
<p>{add(2, 6)}</p>
<div className="flex min-h-svh flex-col items-center justify-center">
<p> apps/web</p>
<Button onClick={() => addUser()}>Add</Button>
<div className="mx-auto w-full max-w-sm">
{JSON.stringify(users, null, 2)}
</div>
</div>
)
Comment on lines 7 to 18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add loading and error states for the Convex query.

useQuery returns undefined while loading and null on error. Currently, JSON.stringify(undefined, null, 2) renders as empty whitespace with no user feedback. Add explicit loading and error handling before rendering results.

 export default function Page() {
   const users = useQuery(api.users.getMany)
   const addUser = useMutation(api.users.add)
+
+  if (users === undefined) {
+    return <div className="flex min-h-svh items-center justify-center">Loading...</div>
+  }
+
+  if (users === null) {
+    return <div className="flex min-h-svh items-center justify-center text-red-500">Error loading users</div>
+  }
+
   return (
     <div className="flex min-h-svh flex-col items-center justify-center">
       <p> apps/web</p>
       <Button onClick={() => addUser()}>Add</Button>
       <div className="mx-auto w-full max-w-sm">
-        {JSON.stringify(users, null, 2)}
+        <pre className="rounded bg-muted p-4 overflow-auto">
+          {JSON.stringify(users, null, 2)}
+        </pre>
       </div>
     </div>
   )
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/app/page.tsx` around lines 7 - 18, The Page component renders the
Convex useQuery result directly, so it shows nothing while the query is loading
or has failed. Update the Page function to check users before JSON.stringify:
show a loading message when users is undefined, an error message when users is
null, and only render the user data once it is available. Use the existing Page
and useQuery symbols to locate the logic and keep the Button/addUser behavior
unchanged.

Expand Down
16 changes: 4 additions & 12 deletions apps/web/components/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
"use client"

import * as React from "react"
import { ConvexProvider, ConvexReactClient } from "convex/react"
import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes"

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL || "")

function ThemeProvider({
children,
...props

Check warning on line 11 in apps/web/components/theme-provider.tsx

View workflow job for this annotation

GitHub Actions / Lint

'props' is defined but never used
}: React.ComponentProps<typeof NextThemesProvider>) {
return (
<NextThemesProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
{...props}
>
<ThemeHotkey />
{children}
</NextThemesProvider>
)
return <ConvexProvider client={convex}>{children}</ConvexProvider>
Comment on lines 9 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git diff -- apps/web/components/theme-provider.tsx -- apps/web/components/theme-provider.tsx || true
printf '\n--- file ---\n'
cat -n apps/web/components/theme-provider.tsx | sed -n '1,220p'
printf '\n--- theme hotkey refs ---\n'
rg -n "ThemeHotkey|useTheme|NextThemesProvider|ConvexProvider" apps/web/components/theme-provider.tsx apps/web -g '!**/node_modules/**'

Repository: RISHII7/echo

Length of output: 3236


Compose ConvexProvider inside NextThemesProvider. This wrapper still takes next-themes props, but it no longer provides theme context or mounts ThemeHotkey, so children lose the class/system-theme behavior and the d hotkey.

🧰 Tools
🪛 GitHub Check: Lint

[warning] 11-11:
'props' is defined but never used

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/components/theme-provider.tsx` around lines 9 - 13, The
ThemeProvider wrapper currently returns only ConvexProvider, so it drops the
NextThemesProvider context and ThemeHotkey behavior. Update ThemeProvider to
compose ConvexProvider inside NextThemesProvider while still forwarding the
next-themes props, and keep ThemeHotkey mounted so children retain
class/system-theme handling and the d hotkey. Use the existing ThemeProvider,
NextThemesProvider, ConvexProvider, and ThemeHotkey symbols to place the
providers in the correct nesting.

}

function isTypingTarget(target: EventTarget | null) {
Expand All @@ -34,7 +26,7 @@
)
}

function ThemeHotkey() {

Check warning on line 29 in apps/web/components/theme-provider.tsx

View workflow job for this annotation

GitHub Actions / Lint

'ThemeHotkey' is defined but never used
const { resolvedTheme, setTheme } = useTheme()

React.useEffect(() => {
Expand Down
2 changes: 2 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@workspace/backend": "workspace:*",
"@workspace/math": "workspace:*",
"@workspace/ui": "workspace:*",
"convex": "^1.42.0",
"lucide-react": "^1.21.0",
"next": "16.2.6",
"next-themes": "^0.4.6",
Expand Down
3 changes: 2 additions & 1 deletion apps/web/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"compilerOptions": {
"paths": {
"@/*": ["./*"],
"@workspace/ui/*": ["../../packages/ui/src/*"]
"@workspace/ui/*": ["../../packages/ui/src/*"],
"@workspace/backend/*": ["../../packages/backend/convex/*"]
},
"plugins": [
{
Expand Down
20 changes: 12 additions & 8 deletions apps/widget/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { add } from "@workspace/math/add"
"use client"

import { useMutation, useQuery } from "convex/react"
import { api } from "@workspace/backend/_generated/api"
import { Button } from "@workspace/ui/components/button"
import { Input } from "@workspace/ui/components/input"

export default function Page() {
const users = useQuery(api.users.getMany)
const addUser = useMutation(api.users.add)

return (
<div className="flex min-h-svh items-center justify-center">
<div className="flex flex-col items-center justify-center gap-4">
<h1 className="text-2xl font-bold">Hello apps/widget</h1>
<Button className="sm">Button</Button>
<p>{add(10, 12)}</p>
<Input />
<div className="flex min-h-svh flex-col items-center justify-center">
<p> apps/widget</p>
<Button onClick={() => addUser()}>Add</Button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== page.tsx ==\n'
sed -n '1,120p' apps/widget/app/page.tsx

printf '\n== users mutation ==\n'
sed -n '1,220p' packages/backend/convex/users.ts

printf '\n== useMutation usages ==\n'
rg -n "useMutation\\(|addUser\\(" apps/widget packages/backend -S

printf '\n== convex/react docs in repo (if any) ==\n'
rg -n "useMutation" . -g '!node_modules' -g '!dist' -g '!build' -S

Repository: RISHII7/echo

Length of output: 2119


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant files precisely and inspect surrounding context.
git ls-files 'apps/widget/app/page.tsx' 'packages/backend/convex/users.ts' || true

printf '\n== page.tsx with line numbers ==\n'
cat -n apps/widget/app/page.tsx | sed -n '1,120p'

printf '\n== users.ts with line numbers ==\n'
cat -n packages/backend/convex/users.ts | sed -n '1,220p'

printf '\n== search for any local error handling around addUser ==\n'
rg -n "addUser\\(|console\\.error|catch \\(" apps/widget -S

Repository: RISHII7/echo

Length of output: 1674


Handle the mutation promise here. addUser() is ignored, so a failed write has no local error path and the user gets no feedback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/widget/app/page.tsx` at line 14, The Add button handler in page.tsx
ignores the promise returned by addUser(), so failures are not handled locally
and the user gets no feedback. Update the onClick path around addUser() to
explicitly handle the mutation promise, using the existing addUser function and
Button handler to await or catch the result and surface an error state or
message if the write fails.

<div className="mx-auto w-full max-w-sm">
{JSON.stringify(users, null, 2)}
</div>
</div>
)
Expand Down
16 changes: 4 additions & 12 deletions apps/widget/components/theme-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,15 @@

import * as React from "react"
import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes"
import { ConvexReactClient, ConvexProvider } from "convex/react"

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL || "")

function ThemeProvider({
children,
...props

Check warning on line 11 in apps/widget/components/theme-provider.tsx

View workflow job for this annotation

GitHub Actions / Lint

'props' is defined but never used
}: React.ComponentProps<typeof NextThemesProvider>) {
return (
<NextThemesProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
{...props}
>
<ThemeHotkey />
{children}
</NextThemesProvider>
)
return <ConvexProvider client={convex}>{children}</ConvexProvider>
}

function isTypingTarget(target: EventTarget | null) {
Expand All @@ -34,7 +26,7 @@
)
}

function ThemeHotkey() {

Check warning on line 29 in apps/widget/components/theme-provider.tsx

View workflow job for this annotation

GitHub Actions / Lint

'ThemeHotkey' is defined but never used
const { resolvedTheme, setTheme } = useTheme()

React.useEffect(() => {
Expand Down
2 changes: 2 additions & 0 deletions apps/widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@workspace/backend": "workspace:*",
"@workspace/math": "workspace:*",
"@workspace/ui": "workspace:*",
"convex": "^1.42.0",
"lucide-react": "^1.21.0",
"next": "16.2.6",
"next-themes": "^0.4.6",
Expand Down
3 changes: 2 additions & 1 deletion apps/widget/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"compilerOptions": {
"paths": {
"@/*": ["./*"],
"@workspace/ui/*": ["../../packages/ui/src/*"]
"@workspace/ui/*": ["../../packages/ui/src/*"],
"@workspace/backend/*": ["../../packages/backend/convex/*"]
},
"plugins": [
{
Expand Down
Loading
Loading