bridgewatercollege/canvas-api-bundle
## Getting Started
1. **Installation**
Add the package via Composer:
```bash
composer require bridgewatercollege/canvas-api-bundle
Publish Configuration Generate the required config file (critical for first-time use):
php artisan vendor:publish --provider="BridgewaterCollege\CanvasApiBundle\CanvasApiServiceProvider" --tag="config"
This resolves the 1.0.0 initialization error.
Configure .env
Add Canvas API credentials to your .env:
CANVAS_API_URL=https://your-institution.instructure.com
CANVAS_API_KEY=your_api_key_here
First Use Case: Fetch a Course
use BridgewaterCollege\CanvasApiBundle\Facades\CanvasApi;
$course = CanvasApi::getCourse($courseId);
dd($course);
API Calls Use facade methods for direct API interactions:
// POST/PUT
$user = CanvasApi::createUser($userData);
$course = CanvasApi::updateCourse($courseId, $updates);
// GET
$section = CanvasApi::getSection($sectionId);
$enrollments = CanvasApi::getUserEnrollments($userId);
Bulk Operations Chain methods for workflows (e.g., course + section creation):
$course = CanvasApi::createCourse($courseData);
$section = CanvasApi::createSection($course['id'], $sectionData);
CanvasApi::enrollSectionUser($section['id'], $userId);
Error Handling Wrap calls in try-catch blocks:
try {
CanvasApi::deleteSection($sectionId);
} catch (\Exception $e) {
Log::error("Canvas API Error: " . $e->getMessage());
// Retry or notify admin
}
Service Provider Binding Extend functionality by binding custom handlers:
$this->app->bind('canvas.api.handler', function ($app) {
return new CustomCanvasHandler($app['canvas.api.client']);
});
Event-Driven Extensions Listen to Canvas API events (e.g., after course creation):
Event::listen('canvas.course.created', function ($course) {
// Trigger Slack notification or update local DB
});
Artisan Commands Create custom commands using the bundle’s client:
class SyncCanvasCoursesCommand extends Command {
protected $canvas;
public function __construct(CanvasApiClient $client) {
$this->canvas = $client;
parent::__construct();
}
public function handle() {
$courses = $this->canvas->getCourses();
// Process courses...
}
}
Missing Config File
If you skip vendor:publish, the package throws a ConfigurationNotFoundException.
Fix: Always run the publish command before use.
API Key Validation The bundle doesn’t validate the API key on first request. Test with:
try {
CanvasApi::getAccount();
} catch (\Exception $e) {
// Handle invalid credentials
}
Silent Failures
Some methods (e.g., deleteEnrolledById) may return false instead of throwing exceptions.
Tip: Add logging:
$result = CanvasApi::deleteEnrolledById($enrollmentId);
if (!$result) {
Log::warning("Failed to delete enrollment #{$enrollmentId}");
}
Rate Limiting Canvas API enforces rate limits. Implement exponential backoff:
use Illuminate\Support\Facades\Http;
$response = Http::retry(3, 100)->get($endpoint);
Time Zone Issues Canvas API uses UTC. Convert responses locally:
$createdAt = Carbon::parse($course['created_at'])->tz('America/New_York');
Custom Endpoints
Extend the CanvasApiClient to add unsupported endpoints:
namespace App\Services;
use BridgewaterCollege\CanvasApiBundle\CanvasApiClient;
class ExtendedCanvasClient extends CanvasApiClient {
public function getCustomEndpoint($endpoint, $params = []) {
return $this->client->get("/api/v1{$endpoint}", $params);
}
}
Bind it in AppServiceProvider:
$this->app->bind('canvas.api.client', function ($app) {
return new ExtendedCanvasClient($app['http.client']);
});
Mocking for Tests Use Laravel’s HTTP mocking:
Http::fake([
'your-institution.instructure.com/*' => Http::response([], 200),
]);
Caching Responses Cache frequent API calls (e.g., course listings):
$courses = Cache::remember("canvas.courses.{$courseId}", now()->addHours(1), function () {
return CanvasApi::getCourse($courseId);
});
GET /api/v1/courses/{course_id}/enrollments).// Bad: Loads all enrollments for every course
$courses = CanvasApi::getCourses(['include' => ['enrollments']]);
// Good: Load enrollments only when required
$course = CanvasApi::getCourse($courseId);
$enrollments = CanvasApi::getSectionEnrollments($course['sections'][0]['id']);
```markdown
## Gotchas and Tips (Continued)
### **Configuration Deep Dive**
- **Environment Overrides**
The bundle respects `.env` for:
```env
CANVAS_API_URL=
CANVAS_API_KEY=
CANVAS_API_VERSION=v1 # Defaults to v1 if unset
Tip: Use php artisan config:cache after changes to avoid runtime overrides.
config/canvas-api.php:
'features' => [
'webhooks' => env('CANVAS_ENABLE_WEBHOOKS', false),
'debug_logging' => env('APP_DEBUG'),
],
| Error | Solution |
|---|---|
Class 'CanvasApi' not found |
Run composer dump-autoload or check facade binding in CanvasApiServiceProvider. |
Token not found |
Verify CANVAS_API_KEY in .env and regenerate the key in Canvas. |
Invalid endpoint |
Ensure API version is set (e.g., /api/v1/...). |
Section not found |
Validate section IDs (Canvas uses numeric IDs, not slugs). |
Webhook Handling
Use Laravel’s HandleIncomingWebhook trait to process Canvas webhooks:
class CanvasWebhookController extends Controller {
use HandleIncomingWebhook;
protected $expectedSignature = 'canvas_webhook_signature';
public function handleWebhook() {
$payload = $this->validateWebhook();
// Process $payload (e.g., update local DB)
}
}
Queue Background Jobs Offload long-running API calls to queues:
CanvasApi::dispatchSyncCourse($courseId)->onQueue('canvas');
Define the job:
class SyncCourseJob implements ShouldQueue {
use Dispatchable, InteractsWithQueue;
public function handle() {
CanvasApi::syncCourseData($this->courseId);
}
}
Testing Strategies Use Laravel’s HTTP tests to mock API responses:
public function test_course_creation() {
Http::fake([
'*.instructure.com/api/v1/courses' => Http::response(['id' => 123], 201),
]);
$response = CanvasApi::createCourse($data);
$response->assertSuccessful();
}
API Key Storage
Never commit .env to version control. Use Laravel’s env() helper:
$key = env('CANVAS_API_KEY');
Rate Limit Protection Implement middleware to throttle requests:
Route::middleware(['throttle:60,1'])->group(function () {
// Canvas API routes
});
Webhook Verification Always verify webhook signatures:
How can I help you explore Laravel packages today?