Implement reliable background jobs and workflows with Inngest in Google Antigravity including retries and scheduling
# Inngest Background Job Patterns for Google Antigravity
Background jobs and workflows are essential for handling long-running tasks, scheduled operations, and event-driven automation. This guide establishes patterns for integrating Inngest with Google Antigravity projects, enabling Gemini 3 to generate reliable, observable background job systems.
## Core Configuration
Set up Inngest client and function definitions:
```typescript
// lib/inngest/client.ts
import { Inngest, EventSchemas } from "inngest";
type Events = {
"user/created": { data: { userId: string; email: string } };
"user/subscription.started": { data: { userId: string; plan: string } };
"order/placed": { data: { orderId: string; userId: string; total: number } };
"email/send": { data: { to: string; template: string; variables: Record<string, unknown> } };
"report/generate": { data: { reportType: string; userId: string; dateRange: { start: string; end: string } } };
};
export const inngest = new Inngest({
id: "my-app",
schemas: new EventSchemas().fromRecord<Events>(),
});
```
## Function Definitions
Create reliable background functions:
```typescript
// lib/inngest/functions/onboarding.ts
import { inngest } from "../client";
import { sendEmail } from "@/lib/email";
import { db } from "@/lib/db";
export const userOnboardingWorkflow = inngest.createFunction(
{
id: "user-onboarding",
retries: 3,
concurrency: { limit: 10 },
},
{ event: "user/created" },
async ({ event, step }) => {
const { userId, email } = event.data;
// Step 1: Send welcome email
await step.run("send-welcome-email", async () => {
await sendEmail({
to: email,
template: "welcome",
variables: { userId },
});
});
// Step 2: Wait 1 day before sending tips email
await step.sleep("wait-for-tips", "1 day");
// Step 3: Check if user has completed profile
const user = await step.run("check-profile", async () => {
return await db.user.findUnique({
where: { id: userId },
select: { profileComplete: true, name: true },
});
});
// Step 4: Send appropriate follow-up
if (!user?.profileComplete) {
await step.run("send-profile-reminder", async () => {
await sendEmail({
to: email,
template: "complete-profile",
variables: { userId, name: user?.name },
});
});
} else {
await step.run("send-tips-email", async () => {
await sendEmail({
to: email,
template: "getting-started-tips",
variables: { userId },
});
});
}
// Step 5: Wait 7 days for feature discovery email
await step.sleep("wait-for-features", "7 days");
await step.run("send-features-email", async () => {
await sendEmail({
to: email,
template: "advanced-features",
variables: { userId },
});
});
return { completed: true, userId };
}
);
```
## Scheduled Jobs
Create cron-based scheduled functions:
```typescript
// lib/inngest/functions/scheduled.ts
import { inngest } from "../client";
import { db } from "@/lib/db";
import { generateReport, sendSlackNotification } from "@/lib/services";
export const dailyReportJob = inngest.createFunction(
{
id: "daily-report",
retries: 2,
},
{ cron: "0 9 * * *" }, // Every day at 9 AM
async ({ step }) => {
// Step 1: Gather metrics
const metrics = await step.run("gather-metrics", async () => {
const [users, orders, revenue] = await Promise.all([
db.user.count({ where: { createdAt: { gte: new Date(Date.now() - 86400000) } } }),
db.order.count({ where: { createdAt: { gte: new Date(Date.now() - 86400000) } } }),
db.order.aggregate({
where: { createdAt: { gte: new Date(Date.now() - 86400000) } },
_sum: { total: true },
}),
]);
return { newUsers: users, orders, revenue: revenue._sum.total || 0 };
});
// Step 2: Generate report
const report = await step.run("generate-report", async () => {
return await generateReport({
type: "daily-summary",
data: metrics,
});
});
// Step 3: Send Slack notification
await step.run("notify-slack", async () => {
await sendSlackNotification({
channel: "#daily-reports",
message: `Daily Report: ${metrics.newUsers} new users, ${metrics.orders} orders, $${metrics.revenue} revenue`,
attachments: [{ title: "Full Report", url: report.url }],
});
});
return { success: true, metrics };
}
);
export const cleanupExpiredSessions = inngest.createFunction(
{ id: "cleanup-sessions" },
{ cron: "0 */6 * * *" }, // Every 6 hours
async ({ step }) => {
const deleted = await step.run("delete-expired", async () => {
const result = await db.session.deleteMany({
where: { expiresAt: { lt: new Date() } },
});
return result.count;
});
return { deletedSessions: deleted };
}
);
```
## API Route Handler
Serve Inngest functions via API route:
```typescript
// app/api/inngest/route.ts
import { serve } from "inngest/next";
import { inngest } from "@/lib/inngest/client";
import { userOnboardingWorkflow } from "@/lib/inngest/functions/onboarding";
import { dailyReportJob, cleanupExpiredSessions } from "@/lib/inngest/functions/scheduled";
export const { GET, POST, PUT } = serve({
client: inngest,
functions: [
userOnboardingWorkflow,
dailyReportJob,
cleanupExpiredSessions,
],
});
```
## Best Practices
1. **Step isolation**: Break workflows into small, retryable steps
2. **Idempotency**: Design steps to be safely re-runnable
3. **Concurrency limits**: Prevent overwhelming external services
4. **Observability**: Use Inngest dashboard for monitoring
5. **Error handling**: Let Inngest handle retries automaticallyThis Background Jobs prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their background jobs implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For Background Jobs projects, consider mentioning your framework version, coding style, and any specific libraries you're using.