Skip to content

Commit

Permalink
Add every v2 endpoint as method, Error improvements and more (PLhery#19)
Browse files Browse the repository at this point in the history
* Installed packages

* Numerous documentation additions & all v2 endpoints & numerous v1 endpoints

* Fixed fetch error & bad documentation

* Auth documentation

* Refereshed README to match real API

* Dropped oauth1 dependency

* Tiny adjustements to request handler

* Dev utilities + geo API

* Subtitle + WebP support in media upload, add/delete subtitle endpoints, add metadata endpoint

* Use raw binary file instead of base64 encoded chunks in media upload

* Only append media part if buffer is not empty

* Simplifications and method split for mediaUpload

* Tweet timelines (home/user/mention) for v1 API

* Moved update stream rules v2 write->read permission

* Removed commander dependency

* Paginator documentation

* Remove spaces between goal items

* Async iterator for TweetStream

* Typings for TweetStream events

* Documentation improvements

* README refactor

* WIP: All README links

* V1 doc until geoSearch

* V1 doc ok

* TypeDoc

* Doc for v2

* User V2 & tweet v2 unit tests

* Exported right clients

* Corrections made for unit test - debug mode for requests

* Added test for mp4 upload & add mediaInfo command

* fix: Correctly affect basic token when copying instance

* fix: Better byteLength calculation for body string
Usage of Buffer.byteLength instead of TextEncoder().encode

* fix: Fixed some edge case in stream tests where Twitter can send a 429

* Changed package description :)

* dep: Bump form-data to 4.0

* Ignored source code (and test folder) when publishing to NPM

* fix: Link to v1/v2 into basics doc

Co-authored-by: Louis Béranger <louis@kidizz.com>
Co-authored-by: Louis Béranger <beranger.louis.bio@gmail.com>
  • Loading branch information
3 people authored Apr 18, 2021
1 parent 0adc592 commit 7bd0b5b
Showing 65 changed files with 5,824 additions and 727 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -23,3 +23,6 @@ node_modules/
.vscode

*.DS_Store

# TypeScript auto-generated doc
tsdocs/
4 changes: 3 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
./src/test/
./src/
./test/
./tsdocs/
./dist/test/
166 changes: 78 additions & 88 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,112 +1,102 @@
# Twitter API V2
# Twitter API v2

Twitter api V1 and V2 client for node
Strongly typed, full-featured, right protected, versatile yet powerful Twitter API v1.1 and v2 client for Node.js.

## How to use
## Why?

```typescript
import TwitterApi, { TwitterErrors } from 'twitter-api-v2';
- The main libraries (twit/twitter) were not updated in a while
- I don't think a Twitter library need many dependencies

// bearer token auth (with V2)
const twitterClient = new TwitterApi('<YOUR_APP_USER_TOKEN>');
They caused me some frustration:
- They don't support video upload in a simple way
- They don't explain well the "link" auth process
- They don't support yet Twitter API V2
- They could have more helpers (for pagination, rate limit, ...)
- Typings could make the difference between read/write app

// token auth
const twitterClient = new TwitterApi({
appKey: '<YOUR-TWITTER-APP-TOKEN>',
appSecret: '<YOUR-TWITTER-APP-SECERT>',
accessToken: '<YOUR-TWITTER-APP-TOKEN>',
accessSecret: '<YOUR-TWITTER-APP-SECERT>',
});
## Goals

// link auth
const twitterClient = new TwitterApi({
appKey: '<YOUR-TWITTER-APP-TOKEN>',
appSecret: '<YOUR-TWITTER-APP-SECERT>',
});
Here's the feature highlights of `twitter-api-v2`:

const authLink = await twitterClient.generateAuthLink();
// ... redirected to https://website.com?oauth_token=XXX&oauth_verifier=XXX
const { usertoken, userSecret } = twitterClient.login('<THE_OAUTH_VERIFIER>');
### Basics:
- Support for v1.1 and **v2 of Twitter API**
- Make signed HTTP requests to Twitter with every Twitter required auth type:
- classic **OAuth 1.0a** authentification for user-context endpoints
- **OAuth2 Bearer token** for app-only endpoints
- **Basic** HTTP Authorization, required for some auth endpoints or Entreprise API
- Helpers for numerous HTTP request methods (`GET`, `POST`, `PUT`, `DELETE` and `PATCH`),
that handle query string parse & format, automatic body formatting and more
- High-class support for stream endpoints, with easy data consumption and auto-reconnect on stream errors

// Tell typescript it's a readonly app
const twitterClient = new TwitterApi(xxx).readOnly;
### Request helpers:
- Automatic paginator for endpoints like user and tweet timelines,
allowing payload consumption with modern asynchronous iterators until your rate-limit is hit
- Convenient methods for authentication - generate auth links and ask for tokens to your users will be a breeze
- Media upload with API v1.1, including **long video & subtitles support**, automatic media type detection,
**chunked upload** and support for **concurrent uploads**
- Dedicated methods that wraps API v1.1 & v2 endpoints, with **typed arguments** and fully **typed responses**
*(WIP - not all public endpoints are available)*
- Bundled parsing of rate limit headers

// Play with the built in methods
### Type-safe first:
- **Typings for tweet, user, media entities (and more) are bundled in this package!**
- Type-safe wrapping of dedicated methods in 3 right level: *DM*/*Read-write*/*Read-only* (just like Twitter API do!) -
you can declare a read-only client - you will only see the methods associated with read-only endpoints

const user = await twitterClient.v2.userByUsername('plhery');
const followers = await twitterClient.v2.followers(user.data.id);
await twitterClient.v1.tweet('Hello, this is a test.');
await twitterClient.v1.uploadMedia(await fs.promises.readFile(path), { type: 'jpg' })

// the search utils
And last but not least, fully powered by native `Promise`s.

## How to use

Install it through your favorite package manager:
```bash
yarn add twitter-api-v2
# or
npm i twitter-api-v2
```

Here's is a quick example of usage:

```ts
import TwitterApi from 'twitter-api-v2';

const results = await twitterClient.v2.search('hello');
console.log(results.tweets); // 10 tweets
await results.fetchNext(100);
await results.fetchNext(100);
console.log(results.tweets, results.rateLimit); // 210 tweets
// Instanciate with desired auth type (here's Bearer v2 auth)
const twitterClient = new TwitterApi('<YOUR_APP_USER_TOKEN>');

// Tell typescript it's a readonly app
const roClient = twitterClient.readOnly;

// Play with the built in methods
const user = await roClient.v2.userByUsername('plhery');
await twitterClient.v1.tweet('Hello, this is a test.');
// You can upload media easily!
await twitterClient.v1.uploadMedia('./big-buck-bunny.mp4');

// Or manually call the API
await twitterClient.v2.get('tweets/search/recent', {query: 'nodeJS', max_results: '100'});
await twitterClient.v2.get('tweets/search/recent', { query: 'nodeJS', max_results: 100 });
const tweets = await twitterClient.get('https://api.twitter.com/2/tweets/search/recent?query=nodeJS&max_results=100');
```

## Why?
### Basics

- The main libraries (twit/twitter) were not updated in a while
You want **to know more about client usage? See [the Basics](./doc/basics.md)**!

- I don't think a Twitter library need many dependencies
### Examples

They caused me some frustration:
- They don't support video upload in a simple way
- They don't explain well the "link" auth process
- They don't support yet Twitter API V2
- They could have more helpers (for pagination, rate limit, ...)
- Typings could make the difference between read/write app
Wanna see that in action? Jump to [Examples part](./doc/examples.md).

## Goals:

- [x] bearer token auth
- [x] token auth
- [x] link auth
- [x] read/write/DM aware typing
- [x] get/post methods
- [x] custom http methods
- [x] streaming
- [ ] Twitter API V2 tweets methods
- [x] Twitter API V2 users methods
- [ ] Auto pagination
- [ ] Error code enums

```typescript
import TwitterApi, { TwitterErrors } from 'twitter-api-v2';

// bearer token auth (with V2)
const twitterClient = new TwitterApi(tokens);

const authLink = await twitterClient.generateAuthLink();
// ... redirected to https://website.com?oauth_token=XXX&oauth_verifier=XXX
const { usertoken, userSecret } = twitterClient.login('<THE_OAUTH_TOKEN>', '<THE_OAUTH_VERIFIER>');

// Search for tweets
const tweets = await twitterClient.tweets.search('nodeJS', { max_results: 100 });

// Auto-paginate
// (also checks if rate limits will be enough after the first request)
const manyTweets = await twitterClient.tweets.search('nodeJS').fetchLast(10000);

// Manage errors
try {
const manyTweets = await twitterClient.tweets.search('nodeJS').fetchLast(100000000);
} catch (e) {
if (e.errorCode === TwitterErrors.RATE_LIMIT_EXCEEDED) {
console.log('please try again later!');
} else {
throw e;
}
}
```
## Authentification

Lost between the different ways to auth inside Twitter API?
Don't know how to implement 3-legged OAuth flow?

See [Authentification part](./doc/auth.md) to know more and have a comprehensive guide a every Twitter authentification process.

## Streaming

See [Streaming part](./doc/streaming.md).
APIs dedicated to streaming are available in [Streaming part](./doc/streaming.md).

## Full package API

Each Twitter endpoint > method association is described in details inside [the v1.1 comprehensive documentation](./doc/v1.md)
and [the v2 comprehensive documentation](./doc/v2.md).
157 changes: 157 additions & 0 deletions doc/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Authentification

This part will guide you through the multiple steps of Twitter API authentification process
inside `twitter-api-v2` package.

Please first see the [Basics](./basics.md) to know how to create a client with your application keys.

***First***, you must know which type of authentification you want to use.

- User authentification (3-legged OAuth 1.0a flow, see [User-wide authentification flow](#user-wide-authentification-flow))
- App-only authentification (Bearer token, see [Application-only authentification flow](#application-only-authentification-flow))
- Basic authentification (couple of username+password, see [Basic authentification flow](#basic-authentification-flow))

## User-wide authentification flow

Many endpoints on the Twitter developer platform use the OAuth 1.0a method to act on behalf of a Twitter account.
For example, if you have a Twitter developer app, you can make API requests on behalf of any Twitter account as long as that user authenticates your app.

This method is **fairly the most complex** of authentification flow options, but it is, at least for now, the **most used method across Twitter API**.

It is named "3-legged" because it is splitted in 3 parts:
- You (the app/server) generate a auth link that is clickable by a external user, and gives you *temporary* access tokens
- The user clicks on the link, approves the application, it gives you a verifier token
- You use the *temporary* access tokens and verifier token to obtain **user-specific** *persistent* access tokens

**NOTE**
> - If you're building a server that serves content for users,
> you need to "remember" (store) some data between the first two steps,
> so be sure you have a available session-like store (file/memory/Redis/...) to share data across same-user requests.
> - Between steps 1 & 2, users are redirected to official Twitter website. That means you need to have ***either***:
> - A dedicated page in your website meant to "welcome back" users that have been sent to Twitter (this is called **oauth callback**)
> - A dedicated input field where users can input a *PIN code* when they accepted you app on Twitter
### Create the auth link

You need to have a client instanciated with your **consumer keys** from Twitter.
```ts
const client = new TwitterApi({ appKey: CONSUMER_KEY, appSecret: CONSUMER_SECRET });
```

To create the authentification link, use `client.generateAuthLink()` method.
**If you choose to redirect users to your website after authentification, you need to provide a callback URL here.**
```ts
const authLink = await client.generateAuthLink(CALLBACK_URL);

// By default, oauth/authenticate are used for auth links, you can change with linkMode
// property in second parameter to 'authorize' to use oauth/authorize
const authLink = await client.generateAuthLink(CALLBACK_URL, { linkMode: 'authorize' });

// Use URL generated
const ... = authLink.url;
```

**IMPORTANT**: You need to store `authLink.oauth_token` and `authLink.oauth_token_secret` somewhere,
because you will need them for step 2.

### Collect temporary access tokens and get persistent tokens

#### Case 1: User has been redirected to your callback URL
When Twitter redirects to your page, it provides two query string parameters: `oauth_token` and `oauth_verifier`.

**NOTE**: If the user refuses app access, `oauth_verifier` will not be provided.

You need to extract those tokens, find the linked `oauth_token_secret` from given `oauth_token` (using your session store!),
then ask for persistent tokens.

Create a client with these tokens as access token, then call `client.login(oauth_verified)` to create a logged client.

An example flow will be written here using the **express** framework, feel free to adapt to your case.

```ts
app.get('/callback', (req, res) => {
// Exact tokens from query string
const { oauth_token, oauth_verifier } = req.query;
// Get the saved oauth_token_secret from session
const { oauth_token_secret } = req.session;

if (!oauth_token || !oauth_verifier || !oauth_token_secret) {
return res.status(400).send('You denied the app or your session expired!');
}

// Obtain the persistent tokens
// Create a client from temporary tokens
const client = new TwitterApi({
appKey: CONSUMER_KEY,
appSecret: CONSUMER_SECRET,
accessToken: oauth_token,
accessSecret: oauth_token_secret,
});

client.login(oauth_verifier)
.then(({ client: loggedClient, accessToken, accessSecret }) => {
// loggedClient is an authentificated client in behalf of some user
// Store accessToken & accessSecret somewhere
})
.catch(() => res.status(403).send('Invalid verifier or access tokens!'));
});
```

#### Case 2: You choose to use a PIN

You need to extract the given PIN and use it as `oauth_verifier`.

Create a client with previously obtain tokens (during link generation step) as access token,
then call `client.login(given_pin)` to create a logged client.

```ts
// Obtain the persistent tokens
// Create a client from temporary tokens
const client = new TwitterApi({
appKey: CONSUMER_KEY,
appSecret: CONSUMER_SECRET,
accessToken: oauth_token, // oauth token from previous step (link generation)
accessSecret: oauth_token_secret, // oauth token secret from previous step (link generation)
});

// Give the PIN to client.login()
const { client: loggedClient, accessToken, accessSecret } = await client.login(GIVEN_USER_PIN);
// loggedClient is an authentificated client in behalf of some user
// Store accessToken & accessSecret somewhere
```

### Finally, get the full logged user object

You can use the method `.currentUser()` on your client.

This a shortcut to `.v1.verifyCredentials()` with a **cache that store user to avoid multiple API calls**.
Its returns a `UserV1` object.

## Application-only authentification flow

App-only flow use a single OAuth 2.0 Bearer Token that authenticates requests on behalf of your developer App.
As this method is specific to the App, it does not involve any users.
This method is typically for developers that need read-only access to public information.

You can instanciate a Twitter API client with two ways:
- If you already know your Bearer token (you can obtain it in the developer portal), you can use it directly in the constructor as a `string`:
```ts
const client = new TwitterApi(MY_BEARER_TOKEN);
```
- Otherwise, if you've stored your consumers (key & secret, the same needed for OAuth 1.0a flow), you can use them to obtain a fresh Bearer token:
```ts
const consumerClient = new TwitterApi({ appKey: CONSUMER_KEY, appSecret: CONSUMER_SECRET });
// Obtain app-only client
const client = await consumerClient.appLogin();
```

## Basic authentification flow

Mainly for **Twitter enterprise APIs**, that require the use of HTTP Basic Authentication.
You must pass a valid email address and password combination for each request.
The email and password combination are the same ones that you will use to access the enterprise API console, and can be editted from within this console.

Use this combination to create your Twitter API client:
```ts
const client = new TwitterApi({ username: MY_USERNAME, password: MY_PASSWORD });
```
Loading

0 comments on commit 7bd0b5b

Please sign in to comment.