appiversity-sdk
v1.0.0
Published
Official Node.js SDK for the Appiversity REST API with workflow support and exceptional developer ergonomics
Maintainers
Readme
Appiversity SDK for Node.js
Official Node.js SDK for the Appiversity REST API with exceptional developer ergonomics.
Features
✨ Simple & Intuitive - Clean API design with excellent IntelliSense support via JSDoc
🔒 Type-Safe - Comprehensive JSDoc annotations for IDE autocomplete and type checking
🚀 Modern - Uses native Node.js fetch API (Node 18+)
📦 Minimal Dependencies - Only dotenv for .env file support
🎯 Error Handling - Returns {data, error} objects instead of throwing
🔧 Configurable - .env file, environment variables, or direct configuration
📚 Well Documented - Extensive documentation and examples
Installation
npm install appiversity-sdkRequirements
- Node.js 18.0.0 or higher (uses native fetch API)
Quick Start
1. Create a .env File
# .env
APPIVERSITY_SDK_API=your-api-key-here
APPIVERSITY_SDK_INSTITUTION=springfield
APPIVERSITY_SDK_BASE_URL=https://appiversity.com # optional, this is the defaultOr set environment variables:
export APPIVERSITY_SDK_API="your-api-key-here"
export APPIVERSITY_SDK_INSTITUTION="springfield"
export APPIVERSITY_SDK_BASE_URL="https://appiversity.com" # optional2. Use the SDK
import { createClient } from 'appiversity-sdk';
// Create client (automatically loads .env file)
const client = createClient();
// Initialize (fetches institution details)
const initResult = await client.initialize();
if (initResult.error) {
console.error('Failed to initialize:', initResult.error.message);
process.exit(1);
}
console.log('Connected to:', initResult.data.name);
// Search for students
const result = await client.searchStudents({ search: 'Smith', limit: 10 });
if (result.error) {
console.error('Error:', result.error.message);
} else {
console.log('Found', result.data.total_count, 'students');
result.data.students.forEach(student => {
console.log(`${student.full_name} (${student.sid})`);
});
}Configuration
Option 1: .env File (Recommended)
Create a .env file in your project root:
# .env
APPIVERSITY_SDK_API=your-64-character-api-key
APPIVERSITY_SDK_INSTITUTION=your-institution-slug
APPIVERSITY_SDK_BASE_URL=https://appiversity.com # optional, defaults to https://appiversity.comimport { createClient } from 'appiversity-sdk';
// Automatically loads .env file
const client = createClient();
await client.initialize();Option 2: Environment Variables
# Required
export APPIVERSITY_SDK_API="your-64-character-api-key"
export APPIVERSITY_SDK_INSTITUTION="your-institution-slug"
# Optional (defaults to https://appiversity.com if not set)
export APPIVERSITY_SDK_BASE_URL="https://appiversity.com"import { createClient } from 'appiversity-sdk';
const client = createClient();
await client.initialize();Option 3: Direct Configuration
import { createClient } from 'appiversity-sdk';
const client = createClient({
apiKey: 'your-api-key',
institutionSlug: 'springfield',
baseUrl: 'https://custom-domain.com' // optional, defaults to https://appiversity.com
});
await client.initialize();Option 4: Mix of Configuration Sources
// Override specific values while using .env for others
const client = createClient({
institutionSlug: 'different-institution'
// apiKey and baseUrl come from .env file or environment variables
});Note: The default base URL is https://appiversity.com. Only set APPIVERSITY_SDK_BASE_URL if you're using a custom domain or self-hosted instance.
API Reference
Client Initialization
createClient(config)
Creates an Appiversity SDK client instance.
/**
* @param {Object} [config] - Configuration object
* @param {string} [config.apiKey] - API key (defaults to APPIVERSITY_SDK_API)
* @param {string} [config.institutionSlug] - Institution slug (defaults to APPIVERSITY_SDK_INSTITUTION)
* @param {string} [config.baseUrl] - Base URL (defaults to APPIVERSITY_SDK_BASE_URL)
* @returns {AppiversitySDK} SDK instance
*/client.initialize()
Initializes the SDK by fetching institution details. Must be called before using other methods.
const result = await client.initialize();
// result = { data: {...}, status: 200 } or { error: {...}, status: 404 }Returns:
{
data: {
id: number,
name: string,
slug: string,
domain: string,
active_year: string
},
status: number
}Response Format
All API methods return an object with either data or error:
// Success
{
data: {...}, // Response data
status: 200 // HTTP status code
}
// Error
{
error: {
error: string, // Error type
message: string, // Human-readable message
status: number // HTTP status code
},
status: 400
}Always check for errors:
const result = await client.searchStudents({ search: 'Smith' });
if (result.error) {
console.error('Error:', result.error.message);
return;
}
// Safe to use result.data
console.log(result.data.students);Institution Methods
listInstitutions()
Get list of all accessible institutions.
const result = await client.listInstitutions();
// result.data.institutions = [{ id, name, slug }, ...]getInstitutionBySlug(slug)
Get institution details by slug.
const result = await client.getInstitutionBySlug('springfield');
// result.data = { id, name, slug, domain, active_year }Student Methods
searchStudents(params)
Search for students by name, ID, or email.
/**
* @param {Object} params
* @param {string} params.search - Search term (required)
* @param {number} [params.skip=0] - Results to skip
* @param {number} [params.limit=10] - Results to return (max 100)
*/
const result = await client.searchStudents({
search: 'Smith',
skip: 0,
limit: 20
});
if (result.data) {
console.log('Total found:', result.data.total_count);
result.data.students.forEach(student => {
console.log(student.full_name, student.email);
});
}Response:
{
data: {
institution: number,
search: string,
skip: number,
limit: number,
total_count: number,
students: [
{
id: number,
sid: string,
full_name: string,
email: string,
current_programs: [{id, name, code}],
current_gpa: number,
credits_earned: number,
credits_attempted: number
}
],
_meta: {
has_more: boolean,
next_skip: number,
returned_count: number
}
}
}getStudent(studentId)
Get detailed information for a specific student.
const result = await client.getStudent(123);
if (result.data) {
const { student, academic_summary, programs, degrees } = result.data;
console.log('Student:', student.full_name);
console.log('GPA:', academic_summary.gpa);
console.log('Credits:', academic_summary.credits_earned);
}Response:
{
data: {
student: {
id, sid, first_name, last_name, full_name,
email, phone, address: {...}
},
academic_summary: {
credits_attempted, credits_earned,
gpa_credits, gpa
},
programs: [
{id, program_id, name, code, type, start_date}
],
degrees: [
{id, degree_id, name, code, type, start_date}
]
}
}getStudentTranscript(studentId)
Get complete academic transcript for a student.
const result = await client.getStudentTranscript(123);
if (result.data) {
const { transcript } = result.data;
console.log('Courses taken:', transcript.courses.length);
console.log('Total credits:', transcript.totals.credits_earned);
console.log('GPA:', transcript.totals.gpa);
transcript.courses.forEach(course => {
console.log(
`${course.term_code}: ${course.course_subject} ${course.course_number} - ${course.grade_letter}`
);
});
}Response:
{
data: {
student: {id, sid, full_name},
transcript: {
courses: [
{
enrollment_id, course_id, course_subject,
course_number, course_title, section_id,
section_number, term_id, term_code,
term_name, academic_year, credits_attempted,
credits_earned, grade_id, grade_letter,
grade_quality_points, grade_gpa
}
],
totals: {
credits_attempted, credits_earned,
gpa_credits, gpa_quality_points, gpa
},
asof: string
}
}
}getStudentSchedule(studentId, termId)
Get student schedule for a specific term.
const result = await client.getStudentSchedule(123, 15);
if (result.data) {
const { term, schedule } = result.data;
console.log('Term:', term.name);
console.log('Courses:', schedule.courses.length);
console.log('Term GPA:', schedule.totals.term_gpa);
schedule.courses.forEach(course => {
console.log(
`${course.course_subject} ${course.course_number} - ${course.grade_letter || 'In Progress'}`
);
});
}Course Methods
searchCourses(params)
Search for courses by subject, number, or title.
const result = await client.searchCourses({
search: 'MATH',
ay: '2023', // optional: filter by academic year
skip: 0,
limit: 20
});
if (result.data) {
result.data.courses.forEach(course => {
console.log(`${course.subject} ${course.number}: ${course.title}`);
});
}Term Methods
searchTerms(params)
Search for terms by code or name.
const result = await client.searchTerms({
search: 'Spring',
skip: 0,
limit: 10
});
if (result.data) {
result.data.terms.forEach(term => {
console.log(`${term.code}: ${term.short_name}`);
});
}Note: The ay parameter is currently not supported by the terms search endpoint.
Section Methods
searchSections(params)
Search for course sections within a term.
const result = await client.searchSections({
search: 'CS',
term: 15, // required: term ID
skip: 0,
limit: 20
});
if (result.data) {
result.data.sections.forEach(section => {
console.log(
`${section.course_subject} ${section.course_number}-${section.section_number}: ${section.enrollment}/${section.max_capacity}`
);
});
}Utility Methods
getInstitutionId()
Get the current institution ID (available after initialization).
const institutionId = client.getInstitutionId();isInitialized()
Check if the client has been initialized.
if (!client.isInitialized()) {
await client.initialize();
}Complete Example
import { createClient } from 'appiversity-sdk';
async function main() {
// Create and initialize client
const client = createClient();
const initResult = await client.initialize();
if (initResult.error) {
console.error('Initialization failed:', initResult.error.message);
process.exit(1);
}
console.log(`Connected to ${initResult.data.name}\n`);
// Search for students
console.log('Searching for students named "Smith"...');
const searchResult = await client.searchStudents({
search: 'Smith',
limit: 5
});
if (searchResult.error) {
console.error('Search failed:', searchResult.error.message);
return;
}
console.log(`Found ${searchResult.data.total_count} students:\n`);
// Get details for first student
const students = searchResult.data.students;
if (students.length > 0) {
const student = students[0];
console.log(`Getting details for ${student.full_name}...`);
const detailResult = await client.getStudent(student.id);
if (detailResult.error) {
console.error('Failed to get details:', detailResult.error.message);
return;
}
const details = detailResult.data;
console.log('\nStudent Details:');
console.log(' Name:', details.student.full_name);
console.log(' SID:', details.student.sid);
console.log(' Email:', details.student.email);
console.log(' GPA:', details.academic_summary.gpa);
console.log(' Credits:', details.academic_summary.credits_earned);
if (details.programs.length > 0) {
console.log(' Programs:');
details.programs.forEach(program => {
console.log(` - ${program.name} (${program.code})`);
});
}
// Get transcript
console.log('\nGetting transcript...');
const transcriptResult = await client.getStudentTranscript(student.id);
if (transcriptResult.error) {
console.error('Failed to get transcript:', transcriptResult.error.message);
return;
}
const transcript = transcriptResult.data.transcript;
console.log(`\nTranscript (${transcript.courses.length} courses):`);
console.log(' Total Credits:', transcript.totals.credits_earned);
console.log(' GPA:', transcript.totals.gpa);
// Show most recent courses
console.log('\n Recent Courses:');
transcript.courses.slice(0, 5).forEach(course => {
console.log(
` ${course.term_code}: ${course.course_subject} ${course.course_number} - ${course.grade_letter || 'N/A'}`
);
});
}
}
main().catch(console.error);Error Handling
The SDK never throws errors. All methods return an object with either data or error.
Checking for Errors
const result = await client.searchStudents({ search: 'Smith' });
if (result.error) {
// Handle error
console.error(`Error (${result.status}):`, result.error.message);
return;
}
// Use data
console.log(result.data.students);Common Error Types
| Error Type | Status | Description |
|------------|--------|-------------|
| Network Error | 0 | Network connectivity issue |
| Unauthorized | 401 | Invalid or missing API key |
| Forbidden | 403 | Insufficient permissions |
| Not Found | 404 | Resource doesn't exist |
| Bad Request | 400 | Invalid parameters |
| Internal Server Error | 500 | Server-side error |
Error Object Structure
{
error: {
error: string, // Error type (e.g., "Unauthorized")
message: string, // Human-readable message
status: number // HTTP status code
},
status: number // Same as error.status
}Pagination
Endpoints that return lists support pagination via skip and limit parameters:
const fetchPage = async (skip, limit) => {
const result = await client.searchStudents({
search: 'Smith',
skip,
limit
});
if (result.error) return [];
return result.data.students;
};
// Fetch first 100 students
const page1 = await fetchPage(0, 50);
const page2 = await fetchPage(50, 50);
const allStudents = [...page1, ...page2];Checking for More Results
const result = await client.searchStudents({ search: 'Smith', limit: 20 });
if (result.data) {
console.log('Returned:', result.data._meta.returned_count);
console.log('Total:', result.data.total_count);
if (result.data._meta.has_more) {
console.log('More results available');
console.log('Next skip:', result.data._meta.next_skip);
}
}TypeScript Support
While the SDK is written in JavaScript, it includes comprehensive JSDoc annotations for excellent TypeScript IntelliSense support.
If you're using TypeScript, your IDE will provide full autocomplete and type checking:
import { createClient } from 'appiversity-sdk';
const client = createClient();
await client.initialize();
// TypeScript will infer types from JSDoc
const result = await client.searchStudents({ search: 'Smith' });
if (result.data) {
// TypeScript knows result.data.students is an array
result.data.students.forEach(student => {
// Autocomplete available for student properties
console.log(student.full_name);
});
}Best Practices
1. Initialize Once
Initialize the client once and reuse it:
// Good: Initialize once
const client = createClient();
await client.initialize();
async function getStudents() {
return client.searchStudents({ search: 'Smith' });
}
// Bad: Initializing multiple times
async function getStudents() {
const client = createClient();
await client.initialize(); // Unnecessary API call
return client.searchStudents({ search: 'Smith' });
}2. Always Check for Errors
// Good: Check error field
const result = await client.searchStudents({ search: 'Smith' });
if (result.error) {
console.error(result.error.message);
return;
}
console.log(result.data.students);
// Bad: Assuming success
const result = await client.searchStudents({ search: 'Smith' });
console.log(result.data.students); // May crash if error3. Use .env File for Sensitive Data
// Good: .env file for sensitive data
// .env file: APPIVERSITY_SDK_API=your-key
const client = createClient();
// Acceptable: Environment variables
export APPIVERSITY_SDK_API="your-key"
const client = createClient();
// Bad: Hardcoding API keys
const client = createClient({ apiKey: 'abc123...' }); // Never commit this!4. Limit API Calls
// Good: Reasonable limits
const result = await client.searchStudents({ search: 'Smith', limit: 20 });
// Avoid: Requesting maximum every time
const result = await client.searchStudents({ search: 'Smith', limit: 100 });Troubleshooting
"API key is required" Error
Make sure you've:
- Created a
.envfile withAPPIVERSITY_SDK_API=your-key, OR - Set the
APPIVERSITY_SDK_APIenvironment variable, OR - Passed
apiKeytocreateClient({ apiKey: '...' })
"Institution slug is required" Error
Make sure you've:
- Created a
.envfile withAPPIVERSITY_SDK_INSTITUTION=slug, OR - Set the
APPIVERSITY_SDK_INSTITUTIONenvironment variable, OR - Passed
institutionSlugtocreateClient({ institutionSlug: '...' })
"Client not initialized" Error
You must call await client.initialize() before using any other methods.
Network Errors
const result = await client.searchStudents({ search: 'Smith' });
if (result.status === 0) {
console.error('Network error:', result.error.message);
// Check network connectivity and baseUrl configuration
}401 Unauthorized
Your API key is invalid or has been revoked. Generate a new key at /account/api-keys.
403 Forbidden
Your API key doesn't have the required permissions. Check your user privies.
Contributing
Contributions are welcome! Please open an issue or pull request on GitHub.
License
MIT License - see LICENSE file for details.
Support
- Documentation: API Documentation
- Issues: GitHub Issues
- API Keys: Manage at
/account/api-keysin the Appiversity web interface
Changelog
1.0.0 (2025-10-29)
- Initial release
- Support for all Appiversity REST API v1 endpoints
- Student search and detail methods
- Student transcript and schedule methods
- Course, term, and section search methods
- Institution discovery methods
- Comprehensive JSDoc annotations
- Zero dependencies
- Node.js 18+ native fetch support
Made with ❤️ by the Appiversity team
