In this post, we’re going to walk through the process of integrating Salesforce with a Next.js application using the jsforce
library. This is especially useful if you need to access Salesforce data from a Next.js app, whether for displaying information or for backend operations.
What is jsforce?
jsforce
(formerly known as Node-Salesforce) is a powerful JavaScript library that allows you to interact with Salesforce’s API. It works both in the browser and with Node.js, making it versatile for various scenarios. When combined with Next.js, a full-stack React framework, you can create a seamless integration between Salesforce and your app’s frontend or backend.
In this tutorial, we’ll cover:
- Building a Basic Next.js App to Fetch Salesforce Accounts: We’ll retrieve a list of accounts from your Salesforce organization using the Salesforce REST API.
- Exploring jsforce Authentication: We’ll walk through different authentication methods, from basic to more secure OAuth2 flows, and troubleshoot common issues.
Step 1: Setting Up a Basic Next.js App
First, let’s create a simple Next.js app to get the accounts from your Salesforce organization. We’ll be using the jsforce
library to handle the API communication. Here’s how to get started:
- Initialize a Next.js project:
npx create-next-app@latest salesforce-nextjs
cd salesforce-nextjs
npm install jsforce
- create a new file called .env.local on the root project
- Create different pages and api routes to show different methods how Next.js connects to Salesforce using
jsforce
and retrieves the account data.
//.env.local sample file
USERNAME=
PASSWORD=
CLIENT_ID=
CLIENT_SECRET=
REDIRECT_URI=
//sample pages
app/username-password/page.tsx
app/username-password-flow/page.tsx
app/webserver-flow/page.tsx
app/dashboard/page.tsx
//sample api routes
api/oauth2/auth/route.ts
api/oauth2/callback/route.ts
Step 2: Understanding jsforce Authentication Methods
There are multiple ways to authenticate with Salesforce using jsforce, ranging from less secure to more secure approaches:
Username-Password SOAP API Login
This method is simple but less secure. It involves providing your Salesforce username and password along with a security token to authenticate. While useful for quick testing, it is not recommended for production use.
import jsforce from "jsforce";
export default async function Page() {
const conn = new jsforce.Connection({
// you can change loginUrl to connect to sandbox or prerelease env.
// loginUrl : 'https://test.salesforce.com'
});
await conn.login(process.env.USERNAME!, process.env.PASSWORD!);
const result = await conn.query("SELECT Id, Name FROM Account LIMIT 15");
return (
<div>
Username Password Page
<ul className="list-disc list-inside">
{result.records.map((record) => (
<li key={record.Id}>{record.Name}</li>
))}
</ul>
</div>
);
}
OAuth2 Authentication
For production applications, OAuth2 is the more secure and preferred way to authenticate. It provides various flows based on your specific use case.
- OAuth2 Username-Password Flow: This flow allows you to log in using the user’s Salesforce credentials, but it is less secure because it requires storing the username and password.
- OAuth2 Web Server Flow: This is a more secure option where you redirect the user to Salesforce to log in. Once authenticated, Salesforce redirects the user back to your app with a session token, which you can use to access the API.
//username-password flow/page.tsx
import jsforce from "jsforce";
export default async function Page() {
const conn = new jsforce.Connection({
oauth2: {
// you can change loginUrl to connect to sandbox or prerelease env.
// loginUrl : 'https://test.salesforce.com',
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI,
},
});
await conn.login(process.env.USERNAME!, process.env.PASSWORD!);
const result = await conn.query("SELECT Id, Name FROM Account LIMIT 15");
return (
<div>
Username Password Page
<ul className="list-disc list-inside">
{result.records.map((record) => (
<li key={record.Id}>{record.Name}</li>
))}
</ul>
</div>
);
}
//webserver-flow/page.tsx
"use client";
import React from "react";
export default function Page() {
const handleLogin = () => {
window.location.href = "/api/oauth2/auth";
};
return (
<div>
<button onClick={handleLogin}>Login to Salesforce</button>
</div>
);
}
//api/oauth2/auth/route.ts
import { OAuth2 } from 'jsforce';
import { NextResponse } from 'next/server';
const oauth2 = new OAuth2({
// you can change loginUrl to connect to sandbox or prerelease env.
// loginUrl : 'https://test.salesforce.com',
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI,
});
export async function GET() {
const authUrl = oauth2.getAuthorizationUrl({scope: 'api id web'})
return NextResponse.redirect(authUrl);
}
- You need to create a “Connected App” in Salesforce to set up this flow.
- Once the user is authenticated, save the session ID in a cookie. Your app can then read this session ID from the cookie and use it to make API requests to Salesforce.
Step 3: Setting Up a Connected App in Salesforce
- Log in to Salesforce.
- Go to Setup and search for “App Manager.”
- Click New Connected App and fill in the required fields, such as callback URL, OAuth scopes, etc.
- Ensure you enable OAuth settings and specify the correct callback URL (e.g.,
https://yourapp.com/api/callback
).
Handling OAuth2 Web Server Flow in Next.js
Here’s how to handle the callback from Salesforce and manage session cookies in Next.js:
//app/api/oauth2/callback/route.ts
import {OAuth2, Connection} from 'jsforce';
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
const oauth2 = new OAuth2({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI,
});
export async function GET(req: Request) {
const { searchParams } = new URL(req.url);
const code = searchParams.get('code');
const error = searchParams.get('error');
if (error) {
return NextResponse.json({ error: 'Salesforce Oauth Error:' + error});
}
if (!code) {
return NextResponse.json({ error: 'No Authorization code found'});
}
try {
const conn = new Connection({ oauth2 });
await conn.authorize(code);
// Set the access token in an HTTP-only, secure cookie
cookies().set({
name: 'salesforce_access_token',
value: conn.accessToken || '', // the access token
httpOnly: true, // for security, the cookie is accessible only by the server
secure: process.env.NODE_ENV === 'production', // send cookie over HTTPS only in production
path: '/', // cookie is available on every route
maxAge: 60 * 60 * 24 * 7, // 1 week
});
// Optionally, you can store the instance URL in a cookie if needed
cookies().set({
name: 'salesforce_instance_url',
value: conn.instanceUrl || '',
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
path: '/',
maxAge: 60 * 60 * 24 * 7, // 1 week
});
return NextResponse.redirect(new URL('/dashboard', req.url));
} catch(err) {
return NextResponse.json({ error: 'Salesforce Oauth Error:' + err});
}
}
//app/dashboard/page.tsx
import jsforce from "jsforce";
import { cookies } from "next/headers";
export default async function page() {
const cookieStore = cookies();
const accessToken = cookieStore.get("salesforce_access_token")?.value;
const instanceUrl = cookieStore.get("salesforce_instance_url")?.value;
let result;
try {
const conn = new jsforce.Connection({
instanceUrl: instanceUrl,
accessToken: accessToken,
});
result = await conn.query("SELECT Id, Name FROM Account LIMIT 15");
} catch (err) {
console.log(err);
return <div>Failed to fetch data from Salesforce</div>;
}
return (
<div>
Web server flow
<ul className="list-disc list-inside">
{result.records.map((record) => (
<li key={record.Id}>{record.Name}</li>
))}
</ul>
</div>
);
}
In this code, after Salesforce authenticates the user, the session token is stored in a cookie for future API calls.
Troubleshooting Common Issues
While integrating Salesforce with a Next.js app, you might run into some common authentication issues:
- Invalid Grant or Authentication Failure:
"error": "Failed to authorize with Salesforce: invalid_grant: authentication failure"
Solution: Ensure that your Salesforce security settings (e.g., IP restrictions) are relaxed for testing purposes. - OAuth Approval Error:
json "error": "OAUTH_APPROVAL_ERROR_GENERIC"
Solution: Double-check your OAuth scopes and make sure the app has the correct permissions in Salesforce.
Wrapping Up
Integrating Salesforce with a Next.js app using jsforce
can unlock powerful possibilities for accessing and managing Salesforce data in your web applications. While the authentication setup may seem complex at first, the OAuth2 web server flow ensures a secure and scalable integration for real-world applications.
For a deeper dive into Salesforce authentication or if you’re encountering further issues, check out my previous post on using Salesforce with Postman.
Watch the video
Feel free to leave a comment or reach out if you have any questions. Happy coding!