Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Payload SDK package #9463

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

feat: add Payload SDK package #9463

wants to merge 1 commit into from

Conversation

r1tsuu
Copy link
Member

@r1tsuu r1tsuu commented Nov 23, 2024

Adds Payload SDK package, which can be used to query Payload REST API in a fully type safe way. Has support for all necessary operations, including auth, type safe select, populate, joins properties and simplified file uploading.

Its interface is very similar to the Local API, can't even notice the difference:
Example:

import { PayloadSDK } from '@payloadcms/sdk'
import type { Config } from './payload-types'

// Pass your config from generated types as generic
const sdk = new PayloadSDK<Config>({
  baseURL: 'https://example.com/api',
})

// Find operation
const posts = await sdk.find({
  collection: 'posts',
  draft: true,
  limit: 10,
  locale: 'en',
  page: 1,
  where: { _status: { equals: 'published' } },
})

// Find by ID operation
const posts = await sdk.findByID({
  id,
  collection: 'posts',
  draft: true,
  locale: 'en',
})

// Auth login operation
const result = await sdk.login({
  collection: 'users',
  data: {
    email: '[email protected]',
    password: '12345',
  },
})

// Create operation
const result = await sdk.create({
  collection: 'posts',
  data: { text: 'text' },
})

// Create operation with a file
// `file` can be either a Blob | File object or a string URL
const result = await sdk.create({ collection: 'media', file, data: {} })

// Count operation
const result = await sdk.count({ collection: 'posts', where: { id: { equals: post.id } } })

// Update (by ID) operation
const result = await sdk.update({
  collection: 'posts',
  id: post.id,
  data: {
    text: 'updated-text',
  },
})

// Update (bulk) operation
const result = await sdk.update({
  collection: 'posts',
  where: {
    id: {
      equals: post.id,
    },
  },
  data: { text: 'updated-text-bulk' },
})

// Delete (by ID) operation
const result = await sdk.delete({ id: post.id, collection: 'posts' })

// Delete (bulk) operation
const result = await sdk.delete({ where: { id: { equals: post.id } }, collection: 'posts' })

// Find Global operation
const result = await sdk.findGlobal({ slug: 'global' })

// Update Global operation
const result = await sdk.updateGlobal({ slug: 'global', data: { text: 'some-updated-global' } })

// Auth Login operation
const result = await sdk.login({
  collection: 'users',
  data: { email: '[email protected]', password: '123456' },
})

// Auth Me operation
const result = await sdk.me(
  { collection: 'users' },
  {
    headers: {
      Authorization: `JWT  ${user.token}`,
    },
  },
)

// Auth Refresh Token operation
const result = await sdk.refreshToken(
  { collection: 'users' },
  { headers: { Authorization: `JWT ${user.token}` } },
)

// Auth Forgot Password operation
const result = await sdk.forgotPassword({
  collection: 'users',
  data: { email: user.email },
})

// Auth Reset Password operation
const result = await sdk.resetPassword({
  collection: 'users',
  data: { password: '1234567', token: resetPasswordToken },
})

// Find Versions operation
const result = await sdk.findVersions({
  collection: 'posts',
  where: { parent: { equals: post.id } },
})

// Find Version by ID operation
const result = await sdk.findVersionByID({ collection: 'posts', id: version.id })

// Restore Version operation
const result = await sdk.restoreVersion({
  collection: 'posts',
  id,
})

// Find Global Versions operation
const result = await sdk.findGlobalVersions({
  slug: 'global',
})

// Find Global Version by ID operation
const result = await sdk.findGlobalVersionByID({ id: version.id, slug: 'global' })

// Restore Global Version operation
const result = await sdk.restoreGlobalVersion({
  slug: 'global',
  id
})

Every operation has optional 3rd parameter which is used to add additional data to the RequestInit object (like headers):

await sdk.me({
  collection: "users"
}, {
  // RequestInit object
  headers: {
    Authorization: `JWT ${token}`
  }
})

To query custom endpoints, you can use the request method, which is used internally for all other methods:

await sdk.request({
  method: 'POST',
  path: '/send-data',
  json: {
    id: 1,
  },
})

Custom fetch implementation and baseInit for shared RequestInit properties:

const sdk = new PayloadSDK<Config>({
  baseInit: { credentials: 'include' },
  baseURL: 'https://example.com/api',
  fetch: async (url, init) => {
    console.log('before req')
    const response = await fetch(url, init)
    console.log('after req')
    return response
  },
})

@qgwr32
Copy link

qgwr32 commented Nov 23, 2024

Can't wait for this. By the way you've got a typo at packages/sdk/src/auth/rseetPassword.ts.

@denolfe
Copy link
Member

denolfe commented Nov 25, 2024

Any reason we couldn't use this for all test suites that use the REST client?

@r1tsuu
Copy link
Member Author

r1tsuu commented Nov 25, 2024

We could and I thought about this. And actually the same can be in e2e's, we have a custom SDK there but untyped

Just thinking it could be as another chore PR, though maybe worth here as well

@r1tsuu r1tsuu force-pushed the feat/payload-sdk branch 2 times, most recently from 5bad58c to 86da15a Compare December 2, 2024 20:03
r1tsuu added a commit that referenced this pull request Dec 13, 2024
Exposes `pagination: false` to REST / GraphQL to improve performance on
large collections by avoiding count query.
This will also be nice for our SDK
#9463 to have the same
properties.
Copy link
Contributor

@DanRibbens DanRibbens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we are calling SDK is really a REST Client. I wonder if we should consider alternate names.

@r1tsuu
Copy link
Member Author

r1tsuu commented Dec 19, 2024

What we are calling SDK is really a REST Client. I wonder if we should consider alternate names.

I think we want to use the same package for the realtime API client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants