@freshbooks/api

中文文档
4.1.0发布于 2024/09/12MITTypeScriptCommonJS近 30 天下载 1万

NodeJS SDK for FreshBooks API

原始使用文档
文档目录6 节
  1. Installation
  2. Usage
  3. Configuring the API client
  4. Get/set data from REST API
  5. Pagination, Filters, and Includes
  6. Data Field Notes

FreshBooks NodeJS SDK

npm node-lts GitHub Workflow Status

The FreshBooks NodeJS SDK allows you to more easily utilize the FreshBooks API.

Installation

Use your favorite package manager to install any of the packages and save to your package.json:

$ npm install @freshbooks/api

# Or, if you prefer yarn
$ yarn add @freshbooks/api

Usage

See the full documentation.

Configuring the API client

Your app will interact with the REST API using the Client object, available from the @freshbooks/api package. The client may be instantiated with a valid OAuth token or provided with a client secret and redirect URI which may then be used to obtain an access token.

Using a pre-generated access token

import { Client } from '@freshbooks/api'

const clientId = process.env.FRESHBOOKS_APPLICATION_CLIENT_ID

// Get token from authentication or configuration
const token = process.env.FRESHBOOKS_TOKEN

// Instantiate new FreshBooks API client
const client = new Client(clientId, {
    accessToken: token,
})

Using a client secret and redirect URI

import { Client } from '@freshbooks/api'

const clientId = process.env.FRESHBOOKS_APPLICATION_CLIENT_ID
const clientSecret = process.env.FRESHBOOKS_APPLICATION_CLIENT_SECRET

// Instantiate new FreshBooks API client
const client = new Client(clientId, {
    clientSecret,
    redirectUri: 'https://your-redirect-uri.com/'
})

// Give this URL to the user so they can authorize your application
const authUrl = client.getAuthRequestUrl()

// This will redirect them to https://your-redirect-uri.com/?code=XXX
const code = ...

// Returns an object containing the access token, refresh token, and expiry date
// Note that this function sets the token on this client instance to automatically
// authenticates all future requests with this client instance
const tokens = client.getAccessToken(code)

Get/set data from REST API

All REST API methods return a response in the shape of:

{
    ok: boolean
    data?: T // model type of result
    error?: Error
}

Example API client call:

try {
    // Get the current user
    const { data } = await client.users.me()

    console.log(`Hello, ${data.id}`)
} catch ({ statusCode, message }) {
    // Handle error if API call failed
    console.error(`Error fetching user: ${statusCode} - ${message}`)
}

Errors

If an API error occurs, the response object contains an error object, with the following shape:

{
    name: string        // Name of the method called
    message: string     // Error message
    statusCode?: string // HTTP Status Code
    errors?: APIError[] // More detailed message if available
}

Not all API calls return a list of specific errors, but if they do, they will be in the form of:

{
    message: string    // Specific error message eg. 'Item not found.'
    errorCode?: number // A error code, if available. Eg. '1012'
    field?: string     // The field that caused the error, if available. Eg. `itemid`
    object?: string    // The resource, if available. Eg. `item`
    value?: string     // The value of the field, if available. Eg. `123432`
}

Examples:

clientData = {}
try {
    const client = await fbClient.clients.single(accountId, 00000)
    console.log('Not called')
} catch (err) {
    console.log(err.name)
    console.log(err.message)
    console.log(err.statusCode)
    console.log(err.errors)
}
/*
Output:
Get Client
Client not found.
404
[
  {
    message: 'Client not found.',
    errorCode: 1012,
    field: 'userid',
    object: 'client',
    value: '00000'
  }
]
*/
clientData = {}
try {
    const client = await fbClient.clients.create(clientData, accountId)
    console.log('Not called')
} catch (err) {
    console.log(err.name)
    console.log(err.message)
    console.log(err.statusCode)
    console.log(err.errors)
}
/*
Output:
Create Client
At least one field among fname, lname, email and organization is required.
422
[
  {
    message: 'At least one field among fname, lname, email and organization is required.',
    errorCode: 7012,
    field: null,
    object: 'client',
    value: ''
  }
]
*/

Pagination, Filters, and Includes

If an endpoint supports searching or custom inclusions via query parameters, these parameters can be specified using a QueryBuilderType. See FreshBooks API - Parameters documentation.

type QueryBuilderType = PaginationQueryBuilder | IncludesQueryBuilder | SearchQueryBuilder

An appropriate method on the API client will support an array of builders:

public readonly invoices = {
    list: (accountId: string, queryBuilders?: QueryBuilderType[]) => Promise<Result<{
        invoices: Invoice[];
        pages: Pagination;
    }>>;
}

Pagination

Pagination results are included in list responses. The data object will contain a pages property, with the following:

{
    page: number  // The current page of results
    pages: number // The number of pages of results
    total: number // The total number of results
    size: number  // The number of results per page
}

To make a paginated call, first create a PaginationQueryBuilder object set using the page and perPage functions and pass it in the list call.

import { PaginationQueryBuilder } from '@freshbooks/api/dist/models/builders/PaginationQueryBuilder'

const paginator = new PaginationQueryBuilder()
paginator.page(1).perPage(10)
let page = 1
let totalPages = 1

while (page <= totalPages) {
    let response = await fbClient.clients.list(accountId, [paginator])
    let { clients, pages } = response.data
    console.log(clients)
    console.log(`Page ${pages.page} of ${pages.pages} pages`)
    console.log(`Showing ${pages.size} per page for ${pages.total} total clients`)

    page++
    totalPages = pages.pages
    paginator.page(page)

    console.log('Clients:')
    clients.map((client) => console.log(client.organization))
}

Search Filters

To filter which results are return by list method calls, construct a SearchQueryBuilder and pass and pass it in the list call.

The SearchQueryBuilder supports the patterns: equals, in, like, between, and boolean.

s = SearchQueryBuilder()
s.in("email", "[email protected]")
console.log(s.build()) // "&search[email][email protected]"

s = SearchQueryBuilder()
s.in("clientids", [123, 456])
console.log(s.build()) // "&search[clientids][]=123&search[clientids][]=456"

s = SearchQueryBuilder()
s.like("email_like", "@freshbooks.com")
console.log(s.build()) // "&search[email_like][email protected]"

s = SearchQueryBuilder()
s.between("amount", 1, 10)
console.log(s.build()) // "&search[amount_min]=1&search[amount_max]=10)"

s = SearchQueryBuilder()
s.between("start_date", date.today())
console.log(s.build()) // "&search[start_date]=2020-11-21"

s = SearchQueryBuilder()
// Boolean filters are mostly used on Project-like resources
s.boolean('complete', false) // "&complete=false"

Example API client call with SearchQueryBuilder:

import { SearchQueryBuilder } from '@freshbooks/api/dist/models/builders/SearchQueryBuilder'

//create and populate SearchQueryBuilder
const searchQueryBuilder = new SearchQueryBuilder()
    .like('address_like', '200 King Street')
    .between('date', { min: new Date('2010-05-06'), max: new Date('2019-11-10') })

try {
    // Get invoices matching search query
    const { data } = await client.invoices.list(accountId, [searchQueryBuilder])

    console.log('Invoices: ', data)
} catch ({ statusCode, message }) {
    // Handle error if API call failed
    console.error(`Error fetching user: ${statusCode} - ${message}`)
}

Includes

To include additional relationships, sub-resources, or data in a response an IncludesQueryBuilder can be constructed which can then be passed into list, single, create, and update calls.

Example API client call with IncludesQueryBuilder:

import { IncludesQueryBuilder } from '@freshbooks/api/dist/models/builders/IncludesQueryBuilder'

//create and populate IncludesQueryBuilder
const includesQueryBuilder = new IncludesQueryBuilder().includes('lines')
console.log(includesQueryBuilder.build()) // "&include[]=lines"

try {
    // Get invoices with included line items
    const { data } = await client.invoices.list(accountId, [includesQueryBuilder])

    console.log('Invoices: ', data)
} catch ({ statusCode, message }) {
    // Handle error if API call failed
    console.error(`Error fetching user: ${statusCode} - ${message}`)
}

Data Field Notes

Dates and Times

For historical reasons, many resources in the FreshBooks API (mostly accounting-releated) return date/times in "US/Eastern" timezone. Some effort is taken to convert these in the models to return Date objects normalized to UTC.

Monetary Values

The FreshBooks API returns most monetary values as a money object containing the amount and a currency code:

export default interface Money {
    amount?: string
    code?: string
}

The amount is returned and stored as a string to prevent floating point precision issues. If you are doing calculations on these values, it is recommended to use a library for decimal arithmetic such as decimal.js, big.js, or bignumber.js

版本与发布记录

@freshbooks/api 共发布 26 个版本,首次发布于 2020/12/15;过去 12 个月发布了 0 个版本。

版本发布日期距今
4.1.0latest2024/09/122 年前
4.0.02023/03/293 年前
4.0.0-alpha.12023/02/153 年前
4.0.0-alpha.02023/02/073 年前
3.3.02022/12/233 年前
3.2.22022/12/223 年前

关于 @freshbooks/api 的常见问题

@freshbooks/api 是什么?

NodeJS SDK for FreshBooks API

如何安装 @freshbooks/api?

在项目目录运行 npm install @freshbooks/api,或使用 pnpm add / yarn add / bun add。

@freshbooks/api 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@freshbooks/api 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@freshbooks/api 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=14。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@freshbooks/api 可以商用吗?

@freshbooks/api 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@freshbooks/api 的下载量有多少?

根据 npm 官方下载统计,@freshbooks/api 在 2026/10/16 – 2026/11/14 这 30 天内被下载 10,282 次,日均约 343 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@freshbooks/api 还在维护吗?

最新版本 4.1.0 发布于 2024/09/12(2 年前);至今共发布 26 个版本,其中过去 12 个月发布了 0 个;首次发布于 2020/12/15。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。