You can easily interface with APIs in Next.js via Sequin. Sequin will sync your API data to a Postgres database so you can write your integration in SQL. Changes you make to your database records will be applied to the API and your database at the same time.

To interact with tables in your Sequin sync in Next.js, you will do the following:

  • Setup your Sequin Sync
  • Connect to Sequin’s Postgres Proxy
  • Set up API route handlers

Setup a Sequin sync

To setup a Sequin sync, Sequin will guide you through authenticating, selecting the data you want to sync, and connecting to your database. Read our getting started guide for step-by-step instructions.

Connect to Sequin’s Postgres Proxy

Sequin uses a Postgres Proxy to interface with your Sequin-synced tables. The Proxy lets Sequin capture inserts, updates, and deletes you make in your database and commit them to the API.

To use Sequin’s Postgres Proxy in your Next.js app, you can treat it as a regular Postgres database.

The following example creates a connection to the Proxy using the pg client:

const { Pool } = require("pg");

const salesforceProxy = new Pool({
  user: process.env.SALESFORCE_DB_USER,
  host: process.env.SALESFORCE_DB_HOST,
  database: process.env.SALESFORCE_DB,
  password: process.env.SALESFORCE_DB_PASSWORD,
  port: process.env.SALESFORCE_DB_PORT,

module.exports = salesforceProxy;

Add .env.local to your project to prevent exposing sensitive environment variables and configuration details:

# Credentials for Sequin Postgres Proxy

Setup your API route handlers

Next.js Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs.

In this setup, a Next.js frontend interacts with a backend server. The backend connects to a PostgreSQL database using pg and exposes API routes. The frontend communicates with these routes to execute database queries and receive responses.

The following is an example of a route handler in Next.js:

// in ./api/salesforce/products/select/route.js
export async function GET(request) {
  // handle `request`

The api directory structure defines the API endpoint. The example above is located in the file ./api/salesforce/products/select/route.js and therefore results in the following endpoint:


Reading Data

With your API data in Postgres, your route handler functions can just use SQL to interface with your data.

Here’s an example function that queries for all Salesforce products in an organization:

import { NextResponse } from "next/server";
import pool from "@/api/service/db";

export async function GET() {
  const result = await pool.query(
    "select * from salesforce.product limit 100;"

  return NextResponse.json({ response: result.rows });

With the route handler defined in the backend, you call it on the frontend to retrieve data. The following function sends a request to api/salesforce/products/select:

async function getAllProducts() {
  const response = await fetch(
      headers: {
        "Content-Type": "application/json",

  return await response.json();

The following is an example of a server component in Next.js, which utilizes this function to fetch the data and subsequently passes it as a prop to a child component:

export default async function Page() {
  const { response: products } = await getAllProducts();

  return <Child data={products} />;

Writing Data

With Sequin, you can also make mutations via your database as well. Inserts, updates, and deletes you make to Sequin-synced tables are first applied to the API. If they pass validation, they’re committed to your database.

Here’s an example of a route handler function that inserts a new record into a Salesforce products table:

import { NextResponse } from "next/server";
import salesforceProxy from "@/api/service/salesforceProxy";
import pool from "@/api/service/db";

export async function POST(request) {
  const body = await request.json();
  const { product_name, type, price } = body;

  const insertQuery = {
    text: `insert into salesforce.product (product_name, type, price)
            values ($1, $2, $3)
            returning id`,
    values: [product_name, type, price],

  try {
    const insertResult = await salesforceProxy.query(query);
    const insertedId = insertResult.rows[0].id;
    const newestProducts = await pool.query(
      "select * from salesforce.product order by inserted_at desc limit 100;"

    return NextResponse.json({
      response: newestProducts.rows,
      lastInsertedId: insertedId,
  } catch (error) {
    return NextResponse.json({ error: error.message }, { status: 400 });

This example reads from your database right after the insert, returning the fresh list of new Salesforce products. The data you just wrote will be available right away.

You can call backend API endpoint from the frontend like so:

const onAdd = async (data) => {
  const response = await fetch("/api/salesforce/products/insert", {
    method: "POST",
    body: JSON.stringify({ }),

  if (!response.ok) {
    throw new Error("Something went wrong");

  // ...

Error handling

If a mutation fails API validation, Sequin returns the API error as a standard Postgres error.

In the previous example, we demonstrated how you can write to Postgres in a try/catch block so you can catch errors:

try {
  const result = await salesforceProxy.query(insertQuery);
  // ...
} catch (error) {
  return NextResponse.json({ ...error }, { status: 400 });

Let’s say you make a mutation to a Salesforce Contact with an invalid email. Salesforce will return a validation error. Your Postgres query will fail. So, the error inside your catch block will look like this:

  name: 'error',
  severity: 'ERROR',
  code: '23502',
  detail: '[Salesforce] Value for `email` was not a valid email',
  schema: 'salesforce',
  table: 'contact',
  routine: 'ErrorRoutine'
  // ...

You can pass this error down to the frontend and display a helpful message to the user.


Sequin transforms APIs like Salesforce into the proper database you always wanted them to be. Using SQL you can query all your third-party API data natively. No pagination, rate limits, or funky syntax. Plus, because your data is in Postgres, you have access to a plethora of tools and libraries to work with your data.

Was this page helpful?