@milesjyoung/zoho-client
v1.0.2
Published
Reusable Zoho CRM helper for cloud jobs and shared business logic. The client is designed so you can keep a refresh token in your cloud secret store and inject it into each runtime instead of baking credentials into app code.
Downloads
28
Readme
Zoho Helper Module
Reusable Zoho CRM helper for cloud jobs and shared business logic. The client is designed so you can keep a refresh token in your cloud secret store and inject it into each runtime instead of baking credentials into app code.
What It Gives You
- Refresh-token based auth with in-memory access-token caching
- Safe concurrent token refresh handling
- Generic authenticated request helper
- COQL query helper
- Reusable per-module CRUD/search helpers
- Environment-variable defaults for quick local use
- Async refresh-token provider support for cloud secret managers
Install Shape
This repo currently exports a CommonJS module:
const { createZohoClient } = require("./src");Getting The Initial Refresh Token
Before this module can run in a cloud environment, you need to complete Zoho's OAuth consent flow once and capture a refresh token. That refresh token is the long-lived secret you store in your cloud secret manager.
Typical setup flow:
- Create or open your Zoho OAuth client in the Zoho API Console.
- Make sure the client has the CRM scopes your app needs, such as record read and update scopes.
- Run the user consent flow in a browser to get an authorization
code. - Exchange that one-time
codefor anaccess_tokenandrefresh_token. - Store the
refresh_tokenin your cloud secret manager and pass it into this module withrefreshTokenorrefreshTokenProvider.
Example token exchange request (use ZohoCRM.modules.all, ZohoCRM.coql.READ at https://api-console.zoho.com/):
curl -i --request POST \
--url "https://accounts.zoho.com/oauth/v2/token" \
--header "Accept: application/json" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "client_id=$ZOHO_CLIENT_ID" \
--data-urlencode "client_secret=$ZOHO_CLIENT_SECRET" \
--data-urlencode "code=$ZOHO_GRANT_TOKEN"The response should include a refresh_token. Capture that once and store it securely. After that, this module handles access-token refresh automatically.
Basic Usage
const { createZohoClient } = require("./src");
const zoho = createZohoClient({
clientId: process.env.ZOHO_CLIENT_ID,
clientSecret: process.env.ZOHO_CLIENT_SECRET,
refreshToken: process.env.ZOHO_REFRESH_TOKEN,
defaultModule: "Deals",
});
const deals = await zoho.module("Deals").coqlWhere({
fields: ["id", "Deal_Name", "Stage"],
where: "Stage = 'Closed Won'",
limit: 50,
});
await zoho.module("Deals").update([
{
id: "5725767000001234567",
Stage: "Closed Won",
},
]);
await zoho.module("Deals").updateOne("5725767000001234567", {
Stage: "Closed Won",
});
await zoho.module("Deals").updateField(
"5725767000001234567",
"Stage",
"Closed Won"
);Fetch A Single Deal By ID
const { createZohoClient } = require("./src");
const zoho = createZohoClient({
clientId: process.env.ZOHO_CLIENT_ID,
clientSecret: process.env.ZOHO_CLIENT_SECRET,
refreshTokenProvider: async () => getSecretValue("zoho-refresh-token"),
});
const dealResponse = await zoho.module("Deals").get("5725767000001234567");
const deal = dealResponse.data?.[0];
console.log(deal);If you prefer using the lower-level request helper directly:
const dealResponse = await zoho.request({
method: "get",
path: "/crm/v6/Deals/5725767000001234567",
});
const deal = dealResponse.data?.[0];Cloud Secret Manager Example
Pass a function when the refresh token lives in AWS Secrets Manager, GCP Secret Manager, Doppler, 1Password, or another provider:
const zoho = createZohoClient({
clientId: process.env.ZOHO_CLIENT_ID,
clientSecret: process.env.ZOHO_CLIENT_SECRET,
refreshTokenProvider: async () => {
return getSecretValue("zoho-refresh-token");
},
});API
createZohoClient(options)
Supported options:
clientIdclientSecretrefreshTokenrefreshTokenProvideraccountsBaseUrlapiBaseUrlcrmVersiondefaultModuletimeoutMsrefreshBufferMs
Environment fallback is supported for:
ZOHO_CLIENT_IDZOHO_CLIENT_SECRETZOHO_REFRESH_TOKENZOHO_ACCOUNTS_URLZOHO_API_BASE_URLZOHO_CRM_VERSIONZOHO_DEFAULT_MODULEZOHO_TIMEOUT_MSZOHO_REFRESH_BUFFER_MS
Top-level helpers
await zoho.request({ method: "get", path: "/crm/v6/Deals" });
await zoho.coql("select id, Deal_Name from Deals where Stage = 'Closed Won' limit 0, 25");
await zoho.getRecordsByCoqlWhere({
moduleName: "Contacts",
fields: ["id", "Email"],
where: "Email is not null",
});Module facade
const contacts = zoho.module("Contacts");
await contacts.get("5725767000001234567");
await contacts.list({ perPage: 50, fields: ["id", "Last_Name", "Email"] });
await contacts.search({ email: "[email protected]" });
await contacts.create({ Last_Name: "Nguyen", Email: "[email protected]" });
await contacts.update([{ id: "5725767000001234567", Phone: "555-0100" }]);
await contacts.updateOne("5725767000001234567", { Phone: "555-0100" });
await contacts.updateField("5725767000001234567", "Phone", "555-0100");
await contacts.upsert([{ Email: "[email protected]", Last_Name: "Nguyen" }]);
await contacts.delete(["5725767000001234567"]);Suggested Next Step
If you want this reused across many projects, the next clean move is to publish this as its own package or place it in a shared internal library repo with:
- a small test suite around token refresh and request retries
- project-specific wrappers like
createCompanyCamZohoThreadClient() - typed definitions if your consumers are in TypeScript
