# OpenAlex client

A comprehensive TypeScript client for the [OpenAlex](https://openalex.org) academic database API. This SDK provides type-safe access to scholarly works, authors, institutions, sources, and concepts.

## Features

- 🎯 **Full TypeScript support** with comprehensive type definitions
- 🔄 **Complete API coverage** for all OpenAlex entities
- ⚡ **Built-in retry logic** with exponential backoff
- 🌐 **Native fetch API** - no external HTTP dependencies
- 📄 **Pagination support** with async generators
- 🔍 **Advanced search capabilities** and filters
- 🏗️ **Modular architecture** with service-based organization
- ⏱️ **Request timeout and abort support**

## Requirements

- Node.js 18+ (for native fetch support)
- TypeScript 5+ (for development)

## Installation

```bash
npm install openalex-client
```

## Quick Start

```typescript
import { OpenAlex } from "openalex-client";

// Initialize the SDK
const openAlex = new OpenAlex({
  userAgent: "MyApp/1.0.0 (mailto:your-email@example.com)",
});

// Search for works
const works = await openAlex.works.list({
  search: "artificial intelligence",
  per_page: 10,
});

console.log(`Found ${works.meta.count} works`);

// Get a specific work by DOI
const work = await openAlex.works.getByDoi("10.1038/nature12373");
console.log(work.title);

// Get author information
const author = await openAlex.authors.get("A2208157607");
console.log(`${author.display_name} has ${author.works_count} works`);
```

## Configuration

```typescript
const openAlex = new OpenAlex({
  baseURL: "https://api.openalex.org", // API base URL
  userAgent: "MyApp/1.0.0", // Required: Your app identifier
  timeout: 30000, // Request timeout in ms
  retries: 3, // Number of retry attempts
  retryDelay: 1000, // Base delay between retries
});
```

## Core Services

### Works Service

```typescript
// Search works
const works = await openAlex.works.list({
  search: "machine learning",
  filter: {
    publication_year: "2023",
    "open_access.is_oa": true,
  },
  sort: "cited_by_count:desc",
});

// Get works by author
const authorWorks = await openAlex.works.getByAuthor("A2208157607");

// Get citations
const citations = await openAlex.works.getCitedBy("W2741809807");

// Get open access works
const oaWorks = await openAlex.works.getOpenAccess({
  filter: { publication_year: "2023" },
});
```

### Authors Service

```typescript
// Get author by ORCID
const author = await openAlex.authors.getByOrcid("0000-0002-1825-0097");

// Get authors from institution
const institutionAuthors = await openAlex.authors.getByInstitution("I27837315");

// Get highly cited authors
const topAuthors = await openAlex.authors.getHighlyCited({ per_page: 50 });

// Get co-authors
const coAuthors = await openAlex.authors.getCoAuthors("A2208157607");
```

### Institutions Service

```typescript
// Get institution by ROR
const institution = await openAlex.institutions.getByRor("05dxps055");

// Get institutions by country
const usInstitutions = await openAlex.institutions.getByCountry("US");

// Get university-type institutions
const universities = await openAlex.institutions.getUniversities();

// Get institution's works
const institutionWorks = await openAlex.institutions.getWorks("I27837315");
```

### Sources Service

```typescript
// Get source by ISSN
const journal = await openAlex.sources.getByIssn("0028-0836");

// Get open access journals
const oaJournals = await openAlex.sources.getOpenAccess();

// Get journals in DOAJ
const doajJournals = await openAlex.sources.getDoajSources();

// Get sources by publisher
const springerJournals =
  await openAlex.sources.getByPublisher("Springer Nature");
```

### Concepts Service

```typescript
// Get concept hierarchy
const hierarchy = await openAlex.concepts.getHierarchy("C41008148");

// Get works related to concept
const conceptWorks = await openAlex.concepts.getWorks("C41008148");

// Get top-level concepts
const topConcepts = await openAlex.concepts.getTopLevel();

// Find similar concepts
const similar = await openAlex.concepts.findSimilar(
  "machine learning algorithms",
);
```

## Advanced Usage

### Pagination

```typescript
// Using async generator for automatic pagination
for await (const work of openAlex.works.paginate({
  search: "climate change",
})) {
  console.log(work.title);
}

// Manual pagination
let page = 1;
let hasMore = true;

while (hasMore) {
  const response = await openAlex.works.list({
    search: "quantum computing",
    page,
    per_page: 100,
  });

  // Process works
  response.results.forEach((work) => {
    console.log(work.title);
  });

  hasMore = response.results.length === 100;
  page++;
}
```

### Complex Filtering

```typescript
const works = await openAlex.works.list({
  filter: {
    "authorships.institutions.country_code": "US",
    publication_year: "2020-2023",
    "open_access.is_oa": true,
    cited_by_count: ">100",
    "concepts.id": "C41008148", // Computer Science
  },
  sort: "publication_date:desc",
});
```

### Grouping and Aggregation

```typescript
const worksByYear = await openAlex.works.list({
  group_by: "publication_year",
  filter: {
    "concepts.id": "C41008148",
  },
});

console.log(worksByYear.group_by); // Aggregated results by year
```

### Field Selection

```typescript
// Select only specific fields to reduce response size
const works = await openAlex.works.list({
  select: "id,title,publication_year,cited_by_count",
  search: "artificial intelligence",
});
```

## Error Handling

```typescript
try {
  const work = await openAlex.works.getByDoi("invalid-doi");
} catch (error) {
  if (error.status_code === 404) {
    console.log("Work not found");
  } else {
    console.error("API Error:", error.message);
  }
}
```

## Rate Limiting and Best Practices

- **Use a descriptive User-Agent**: Include your email for faster support
- **Be respectful**: Don't make excessive requests
- **Use filters**: Be specific to reduce response sizes
- **Implement caching**: Cache responses when appropriate
- **Handle errors gracefully**: Implement proper error handling

## Development

```bash
# Install dependencies
npm install

# Build the project
npm run build

# Run linting
npm run lint

# Fix linting issues
npm run lint:fix

# Clean build directory
npm run clean
```

## API Reference

For detailed API documentation, visit [OpenAlex API Documentation](https://docs.openalex.org/).

## Contributing

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Ensure all tests pass
6. Submit a pull request

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Support

- 📖 [OpenAlex Documentation](https://docs.openalex.org/)
- 💬 [OpenAlex Community](https://github.com/ourresearch/openalex-guts/discussions)
- 🐛 [Report Issues](https://github.com/pepearce/openalex-client/issues)

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for version history and updates.
