Skip to content

Typescript XDK Completely Botched (Tweet Lookups) #13

@larron

Description

@larron

Get Tweet and Tweets By ID are not returning nearly any of the fields being requested regardless of passing them.
I replaced the entire XDK with a normal fetch and am now receiving the requested data.

This file will give you an idea of exactly how to replicate it by swapping the functions back and uncommenting the import.

// import { Client } from '@xdevplatform/xdk'

const TWEET_FIELDS = {
  expansions: [
    // 'article.cover_media',
    // 'article.media_entities',
    'attachments.media_keys',
    // 'attachments.media_source_tweet',
    // 'attachments.poll_ids',
    'author_id',
    // 'edit_history_tweet_ids',
    'entities.mentions.username',
    // 'geo.place_id',
    'in_reply_to_user_id',
    // 'entities.note.mentions.username',
    // 'referenced_tweets.id',
    // 'referenced_tweets.id.attachments.media_keys',
    // 'referenced_tweets.id.author_id',
  ],

  'tweet.fields': [
    // 'article',
    'attachments',
    'author_id',
    // 'card_uri',
    'community_id',
    // 'context_annotations',
    'conversation_id',
    'created_at',
    // 'display_text_range',
    // 'edit_controls',
    // 'edit_history_tweet_ids',
    'entities',
    // 'geo',
    'id',
    'in_reply_to_user_id',
    'lang',
    'media_metadata',
    // 'non_public_metrics',
    // 'note_tweet',
    // 'organic_metrics',
    'possibly_sensitive',
    // 'promoted_metrics',
    'public_metrics',
    // 'referenced_tweets',
    // 'reply_settings',
    // 'scopes',
    // 'source',
    // 'suggested_source_links',
    'text',
    // 'withheld',
  ],

  'user.fields': [
    // 'affiliation',
    'confirmed_email',
    // 'connection_status',
    // 'created_at',
    'description',
    // 'entities',
    'id',
    'is_identity_verified',
    'location',
    // 'most_recent_tweet_id',
    'name',
    // 'parody',
    // 'pinned_tweet_id',
    'profile_banner_url',
    'profile_image_url',
    'protected',
    'public_metrics',
    // 'receives_your_dm',
    // 'subscription',
    // 'subscription_type',
    'url',
    'username',
    'verified',
    'verified_followers_count',
    'verified_type',
    // 'withheld',
  ],

  'media.fields': [
    'alt_text',
    'duration_ms',
    'height',
    'media_key',
    // 'non_public_metrics',
    // 'organic_metrics',
    'preview_image_url',
    // 'promoted_metrics',
    'public_metrics',
    'type',
    'url',
    'variants',
    'width',
  ],

  // 'place.fields': [
  //   'contained_within',
  //   'country',
  //   'country_code',
  //   'full_name',
  //   'geo',
  //   'id',
  //   'name',
  //   'place_type',
  // ],
  //
  // 'poll.fields': [
  //   'duration_minutes',
  //   'end_datetime',
  //   'id',
  //   'options',
  //   'voting_status',
  // ],
}

// export function getXClient(bearerToken: string) {
//   const config = { bearerToken }
//   return new Client(config)
// }
//
// export async function getTweetFromXApi(tweetId: string, bearerToken: string) {
//   const client = getXClient(bearerToken)
//   return await client.posts.getById(tweetId, TWEET_FIELDS)
// }
//
// export async function getTweetsFromXApi(
//   tweetIds: string[],
//   bearerToken: string,
// ) {
//   const client = getXClient(bearerToken)
//   return await client.posts.getByIds(tweetIds, TWEET_FIELDS)
// }

const API_BASE = 'https://api.x.com/2'

function buildQueryParams(fields: Record<string, any>): URLSearchParams {
  const params = new URLSearchParams()

  // expansions
  if (fields.expansions?.length) {
    params.append('expansions', fields.expansions.join(','))
  }

  // tweet.fields, user.fields, media.fields … (any key that ends with ".fields")
  for (const [key, value] of Object.entries(fields)) {
    if (key.endsWith('.fields') && Array.isArray(value) && value.length) {
      params.append(key, value.join(','))
    }
  }

  return params
}

export async function getTweetFromXApi(
  tweetId: string,
  bearerToken: string,
): Promise<any> {
  const url = new URL(`${API_BASE}/tweets/${tweetId}`)
  url.search = buildQueryParams(TWEET_FIELDS).toString()

  const res = await fetch(url.toString(), {
    method: 'GET',
    headers: {
      Authorization: `Bearer ${bearerToken}`,
      'User-Agent': 'X-Fetch-Client/1.0',
    },
  })

  if (!res.ok) {
    const err = await res.text()
    throw new Error(`X API error ${res.status}: ${err}`)
  }

  return await res.json()
}

export async function getTweetsFromXApi(
  tweetIds: string[],
  bearerToken: string,
): Promise<any> {
  if (!tweetIds.length) return { data: [] }

  const url = new URL(`${API_BASE}/tweets`)
  url.searchParams.append('ids', tweetIds.join(',')) // ids=… is required
  url.search = buildQueryParams(TWEET_FIELDS).toString() // append the rest

  const res = await fetch(url.toString(), {
    method: 'GET',
    headers: {
      Authorization: `Bearer ${bearerToken}`,
      'User-Agent': 'X-Fetch-Client/1.0',
    },
  })

  if (!res.ok) {
    const err = await res.text()
    throw new Error(`X API error ${res.status}: ${err}`)
  }

  return await res.json()
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions