Quickstart
Install and configure the Resend fragment
Overview
The Resend fragment gives you typed APIs for sending email, receiving webhook events, and reading local email threads from your database.
Prerequisites
- A Resend API key + webhook signing secret
- (For sending) A verified sender address for
defaultFrom
Agentic Installation
npx skills add https://github.com/rejot-dev/fragno --skill fragnoThe Fragno agent skill will guide your agent through the installation process. Just tell it to "Integrate the Resend fragment into my application" and it'll go to work. Read the documentation below to see the full process.
Manual Installation
npm install @fragno-dev/resend-fragment @fragno-dev/dbThe CLI will be used to generate the database schema for your ORM and/or apply migrations.
npm install --save-dev @fragno-dev/cli1. Initialize the database adapter
You must initialize the appropriate database adapter for use with the Resend fragment. For additional details on setting the adapter, see our Fragno DB docs.
The example below uses Drizzle ORM with PostgreSQL, but adapters for other databases and ORMs are also available.
import { SqlAdapter } from "@fragno-dev/db/adapters/sql";
import { PostgresDialect } from "@fragno-dev/db/dialects";
import { NodePostgresDriverConfig } from "@fragno-dev/db/drivers";
import { Pool } from "pg";
const dialect = new PostgresDialect({
// You may reuse the connection pool from elsewhere in your application.
pool: new Pool({ connectionString: process.env.DATABASE_URL }),
});
export const fragmentDbAdapter = new SqlAdapter({
dialect,
driverConfig: new NodePostgresDriverConfig(),
});Reference
Available built-in dialects:
- SQL:
SqliteDialect,PostgresDialect,MysqlDialect(from@fragno-dev/db/dialects) - Cloudflare Durable Objects (SQLite):
DurableObjectDialect(from@fragno-dev/db/dialects/durable-object)
Available driver configs (@fragno-dev/db/drivers):
- SQLite:
BetterSQLite3DriverConfig,SQLocalDriverConfig,CloudflareDurableObjectsDriverConfig - PostgreSQL:
NodePostgresDriverConfig,PGLiteDriverConfig - MySQL/MariaDB:
MySQL2DriverConfig
MySQL is experimental
Our focus has been on PostgreSQL and SQLite. MySQL support is experimental and may not work as expected.
2. Create the Resend instance
Create a file to instantiate the fragment server:
import { createResendFragment } from "@fragno-dev/resend-fragment";
import { fragmentDbAdapter } from "@/db/fragno";
export const resendFragment = createResendFragment(
{
apiKey: process.env.RESEND_API_KEY!,
webhookSecret: process.env.RESEND_WEBHOOK_SECRET!,
defaultFrom: "Support <support@example.com>",
defaultReplyTo: "support@example.com",
onEmailReceived: async ({ emailMessageId }) => {
console.log("Inbound email", emailMessageId);
},
onEmailStatusUpdated: async ({ emailMessageId, status }) => {
console.log("Email status updated", emailMessageId, "status", status);
},
},
{
databaseAdapter: fragmentDbAdapter,
// This is also the default mountRoute.
mountRoute: "/api/resend",
},
);Set defaultFrom unless you always provide from in each request.
About mountRoute
mountRoute is the URL prefix where your app exposes this fragment’s HTTP routes. If you change it
server-side, you must use the same mountRoute in the client setup below.
3. Mount the fragment routes
Mount the fragment handlers in your app. See Frameworks for supported runtimes.
Note that most full-stack frameworks use file-based routing, so make sure to create the file in the
correct location based on the mountRoute you configured.
import { resendFragment } from "@/lib/resend";
export const { GET, POST, PUT, PATCH, DELETE } = resendFragment.handlersFor("next-js");4. Generate the database schema
Generate the fragment schema with the CLI:
npx fragno-cli db generate lib/resend.ts --format drizzle -o db/resend.schema.tsThis command will generate a Drizzle schema file that you can integrate into your existing Drizzle setup. You can then use Drizzle to run migrations against your database. We also support Prisma, direct SQL migration file output, and direct migration execution.
For complete database integration instructions, see Database Fragments.
5. Secure routes
Use middleware to require authentication for the fragment’s HTTP APIs (sending mail, listing threads, domains, and so on).
Do not protect the webhook route with your user session: Resend calls it from their
infrastructure. The fragment validates those requests using your webhookSecret and Svix signature
headers instead.
import { createResendFragment } from "@fragno-dev/resend-fragment";
import { fragmentDbAdapter } from "@/db/fragno";
export const resendFragment = createResendFragment(
{
// ... your config from step 2
},
{
databaseAdapter: fragmentDbAdapter,
mountRoute: "/api/resend",
},
).withMiddleware(async ({ path, headers }, { error }) => {
if (path === "/webhook") {
return undefined;
}
const isAuthorized = getUser(headers)?.role === "admin"; // Using your authentication system
if (!isAuthorized) {
return error({ message: "Not authorized", code: "NOT_AUTHORIZED" }, 401);
}
});6. Register the Resend webhook
Assuming you mounted the fragment at /api/resend, go to the
Resend dashboard and register the webhook by pointing it at:
https://your-app.com/api/resend/webhookWe listen to all email.* events.
Client setup
Create a client instance to use the fragment in your frontend:
import { createResendFragmentClient } from "@fragno-dev/resend-fragment/react";
export const resendClient = createResendFragmentClient({
mountRoute: "/api/resend",
});Using the Resend library
See the next page for usage examples.