Connecting a web application to Salesforce is rarely about a single request. It is about designing an authorization model, choosing the right API surface for each feature, keeping transport code out of your business logic, and staying inside the limits the platform enforces. This guide walks through that work in the order a real integration project usually encounters it: what the REST API gives you, how clients get authorized, which identity model fits your product, how wrapper libraries change the size of the code you have to maintain, and how to structure endpoints so the integration keeps working as traffic and feature scope grow.
What the REST API Actually Does
The Salesforce REST API lets a web application access, create, manipulate, and search Salesforce data through HTTP requests sent to API endpoints, entirely without using the standard Salesforce user interface. In practice, that means your application can treat the CRM as a backend service: the browser or server issues conventional HTTP calls, and the response bodies carry the record data your application works with. For a product team, the benefit is architectural rather than cosmetic. Your front end, your Node or Python service layer, and your background workers all speak the same plain HTTP dialect, and none of them needs to embed or automate the CRM interface itself.
A second surface matters when the product you are building reaches beyond core CRM records. The Connect REST API supports integration of web apps, mobile apps, and intranet sites with Salesforce features such as B2B Commerce, CMS content, Experience Cloud sites, and Chatter feeds. If your roadmap includes a storefront, a content-driven site, or feed-based collaboration, those capabilities arrive through Connect endpoints rather than through ordinary record access, so the boundary between the two surfaces should be drawn early in design reviews instead of discovered late in development.
Practically, this shapes how teams divide work. Record-heavy screens—account lists, case histories, opportunity pipelines—map cleanly onto REST API calls against data resources. Experience features map onto Connect endpoints. Keeping those two tracks separate in your service layer prevents a single tangled client module from mixing data access, content delivery, and feed handling, which is exactly the kind of coupling that makes later refactoring expensive.
OAuth 2.0: Authorization Before Anything Else
Nothing meaningful happens before authentication. Every REST API request requires an access token obtained through authentication in order to execute actions against Salesforce resources, and that token travels with each call as the proof of who is making the request. Treat this as a hard dependency in your build order: get a token, verify it against a trivial call, and only then start writing resource code. Teams that reverse this order tend to debug business logic against requests that were never authorized in the first place.
Client applications must be authorized through external client apps or connected apps using OAuth 2.0 authorization flows before they can reach Salesforce REST API resources. That authorization step is where you declare what your application is allowed to do and how it identifies itself. Two implementation habits pay off immediately. First, keep the OAuth handling in a dedicated module—a token provider with one job—so the rest of the codebase asks for an authenticated client rather than assembling headers by hand. Second, keep credentials and tokens on the server side. A browser should call your backend, and your backend should call Salesforce; that way, secrets stay out of client bundles and token storage is your problem to solve once, in one place.
There is also a platform change worth planning around: starting in Spring '26, creation of new connected apps is restricted, and Salesforce recommends external client apps for modern REST API integrations. If you are starting a project now, or maintaining an application that still provisions classic connected apps in its setup script, budget time to move configuration to external client apps. Existing integrations are not the issue here; the friction appears when a new environment has to be spun up and the old provisioning path is no longer available to you.
Once the flow is working, handle the access token as a bearer credential with a lifecycle: retrieve it through the authorization flow, attach it to each request, refresh or re-acquire it when calls start failing with authorization errors, and never log it. Centralizing this in the token provider means those rules are written once instead of copied into every service call site.
Choosing an Identity Model
The most consequential design decision is whose authority your requests run under. In app-to-app integrations, REST API calls execute as a single integration user, whereas custom user interfaces use OAuth to authorize requests on behalf of individual named users. Neither pattern is universally correct; they answer different product questions.
Use the single integration user pattern for background synchronization, imports, scheduled jobs, and system-to-system flows where no person is at the keyboard. It keeps configuration simple: one identity, one authorization setup, and one place to look when requests fail. The trade-off is attribution. Because everything runs under that shared identity, records created or modified by your integration are not distinguishable by who triggered the action unless your own application records that context somewhere in the data it writes.
Use per-user OAuth for interactive web interfaces where actions should be performed as the person using the application. Sales representatives updating an opportunity through your custom dashboard, support agents closing cases in a tailored console, or managers approving records in an intranet tool all expect their own permissions and their own audit trail to apply. The cost is more moving parts: your application must manage authorization for each user, handle the possibility that individual access is revoked, and design screens around the permissions users actually hold.
Many products end up with both, and that is fine. Interactive surfaces run per-user, while the heavy lifting—batch imports, periodic reconciliation, aggregate reporting—runs under the integration user. The important part is deciding deliberately and documenting which flows belong to which identity, because mixing the two patterns accidentally is a common source of both permission errors and confusing audit records.
Wrapper Libraries Reduce Integration Code
Most of the code in a hand-written integration is not business logic. It is token attachment, header construction, serialization, status handling, and retry bookkeeping. Wrapper libraries available in languages such as Node.js, Python, Ruby, and Java simplify REST API integration by managing that low-level authentication and HTTP request and response handling for you.
The payoff is that your team writes code at the level of the problem: fetch these records, create that object, search for matching entries. The library underneath deals with the mechanics of getting an authenticated request onto the wire and turning the response into something your language can consume. When evaluating a wrapper for your stack, look for three things. It should own the authentication flow rather than expecting you to hand it a ready-made header on every call. It should expose request and response handling that your existing logging and error tracking can hook into. And it should let you drop down to raw requests when a feature is not covered, so the library never becomes a ceiling on what you can build.
A minimal client built around a wrapper tends to look roughly like this: obtain a token through the authorization flow, hand the wrapper an authenticated session, and let business code call named operations. The exact syntax differs across Node.js, Python, Ruby, and Java, but the layering is the same—authentication in one place, transport in the library, domain logic in your code. Keep that separation even when a quick prototype tempts you to inline the HTTP calls; prototypes have a way of shipping.
Designing Endpoint Architecture for Efficiency
Efficiency in an integration like this is mostly about call discipline. Every round trip between your web application and Salesforce costs latency, so the guiding question for each screen or job is how few calls can deliver what the user needs. A dashboard that fires a dozen small requests will feel slower and behave worse under load than one that asks an orchestration layer on your server to assemble the same view in fewer, larger exchanges.
A practical pattern is to put a thin aggregation layer between your web client and the API. The browser talks to your own endpoints, which return exactly the shape the UI needs; the aggregation layer composes that shape from Salesforce calls. This also concentrates caching, error handling, and throttling in one service instead of spreading it across every front-end module. When a response is unlikely to change between requests, cache it for a short, explicit window and be honest about staleness in the UI.
Separate read paths from write paths as well. Reads can be batched, cached, and served to many users at once; writes need validation, clear error surfaces, and per-user authority when the identity model calls for it. Background work that does not need to block a user should move off the request cycle entirely, into queued jobs that can retry calmly when a call fails, rather than failing a page load because a single update did not succeed on the first attempt.
Finally, instrument the integration from day one. Count calls per screen, track latency per endpoint, and log failures with enough context to reproduce them—without logging tokens or sensitive record data. The teams that scale an integration successfully are usually the ones that can answer "why did this screen get slower" with data instead of intuition.
Custom API Logic, Governor Limits, and Capacity
Some requirements cannot be satisfied by calling standard endpoints from the outside. When validation has to run inside the platform, when a workflow needs to expose a purpose-built endpoint, or when data must be shaped before it leaves Salesforce, the work moves to custom Apex REST endpoints and server-side logic. That is also where governor limits enter the picture: platform limits constrain how much work custom code can do in a single transaction, so efficient endpoint design is not optional. Queries have to be selective, loops over large data sets have to be reshaped, and bulk operations have to be handled in a way that respects what the platform allows in one execution context.
Designing and tuning that layer is a discipline of its own, and backlogs here tend to grow faster than internal teams can absorb. When integration requirements demand custom Apex REST endpoints or dedicated engineering capacity, organizations frequently opt to hire salesforce api developers to build and manage custom API logic. Whether that capacity arrives as individual contributors joining an existing team or as a group that owns a workstream end to end, the goal is the same: keep custom endpoint architecture, limit-aware code, and API maintenance from stalling the rest of the roadmap.
Pairing external capacity with internal ownership works best when the boundary is explicit. Define who reviews custom endpoints, who owns deployment, and which conventions govern error responses and versioning before the first line of Apex is written. Custom API code is long-lived infrastructure; the cost of unclear ownership shows up months later, when a change is needed and no one is certain what the endpoint is allowed to do.
Practical Checklist
Before you call an integration done, walk through these points:
- The correct API surface is used for each feature: data access through REST API, experience and content features through Connect REST API.
- Authorization uses external client apps or connected apps with OAuth 2.0, and the setup reflects the Spring '26 restriction on creating new connected apps.
- Every request carries a valid access token obtained through authentication, managed in one token provider module.
- The identity model is deliberate: a single integration user for app-to-app flows, per-user OAuth where actions should run as named users.
- Wrapper libraries in your language of choice handle authentication and HTTP request and response handling, with a documented escape hatch for raw calls.
- Call volume per screen or job is measured, aggregation happens server-side, and caching boundaries are explicit.
- Custom Apex endpoints are designed with platform governor limits in mind, and ownership of that code is documented.
None of these steps is exotic on its own. Together, they determine whether a Salesforce integration remains a maintainable part of your architecture or becomes the component everyone is afraid to change. Start with authorization, choose your identity model on purpose, delegate transport to a library, and keep the number of calls small and observable. The rest of the work is ordinary engineering—and that is exactly what you want it to be.
