Convex is a reactive backend platform that replaces your database, server functions, and realtime layer — all with TypeScript.
Server Functions
// convex/tasks.ts
import { query, mutation } from './_generated/server'
import { v } from 'convex/values'
export const list = query({
args: { limit: v.optional(v.number()) },
handler: async (ctx, args) => {
return ctx.db
.query('tasks')
.order('desc')
.take(args.limit ?? 10)
}
})
export const create = mutation({
args: { text: v.string() },
handler: async (ctx, args) => {
return ctx.db.insert('tasks', {
text: args.text,
completed: false,
createdAt: Date.now()
})
}
})
Realtime — Automatic
Queries are automatically reactive:
// React component — updates in realtime!
function TaskList() {
const tasks = useQuery(api.tasks.list, { limit: 20 })
const createTask = useMutation(api.tasks.create)
return (
<div>
<button onClick={() => createTask({ text: 'New task' })}>
Add Task
</button>
{tasks?.map(task => (
<div key={task._id}>{task.text}</div>
))}
</div>
)
}
// When ANY client adds a task, ALL clients see it instantly
HTTP Actions — External API
// convex/http.ts
import { httpRouter } from 'convex/server'
import { httpAction } from './_generated/server'
const http = httpRouter()
http.route({
path: '/api/webhook',
method: 'POST',
handler: httpAction(async (ctx, request) => {
const body = await request.json()
await ctx.runMutation(api.tasks.create, { text: body.text })
return new Response(JSON.stringify({ ok: true }), { status: 200 })
})
})
export default http
Scheduled Functions (Cron)
// convex/crons.ts
import { cronJobs } from 'convex/server'
const crons = cronJobs()
crons.interval('cleanup old tasks', { hours: 24 }, api.tasks.cleanup)
crons.cron('daily report', '0 9 * * *', api.reports.generateDaily)
export default crons
File Storage
// Generate upload URL
export const generateUploadUrl = mutation(async (ctx) => {
return await ctx.storage.generateUploadUrl()
})
// Store file reference
export const saveFile = mutation({
args: { storageId: v.id('_storage'), name: v.string() },
handler: async (ctx, args) => {
return ctx.db.insert('files', {
storageId: args.storageId,
name: args.name,
url: await ctx.storage.getUrl(args.storageId)
})
}
})
Real-World Use Case
A team building a collaborative tool was maintaining PostgreSQL + Redis + WebSocket server + cron jobs. They moved to Convex: mutations auto-sync to all clients, scheduled functions replaced cron, and they deleted their entire WebSocket layer. Backend code went from 3000 lines to 800.
Convex is what Firebase promised but never delivered.
Build Smarter Data Pipelines
Need to scrape websites, extract APIs, or automate data collection? Check out my ready-to-use scrapers on Apify — no coding required.
Custom scraping solution? Email me at spinov001@gmail.com — fast turnaround, fair prices.
Top comments (0)