# How to Integrate Bolt.new with Google Classroom

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: April 2026

## TL;DR

Google Classroom API lets your Bolt.new app read courses, assignments, and rosters from Google Classroom. The integration uses OAuth 2.0 with education scopes — authentication callbacks only work on deployed sites, not in Bolt's WebContainer preview. Build a Next.js API route to handle the OAuth flow, then deploy to Netlify or Bolt Cloud to complete testing.

## Building Educational Tools on Top of Google Classroom

Google Classroom is used by over 150 million students and teachers worldwide, making it one of the most data-rich educational platforms available. Its REST API exposes courses, coursework (assignments and questions), student rosters, grades, and submissions — everything you need to build supplementary tools like assignment trackers, progress dashboards, parent portals, or automated grading aids.

The integration path uses OAuth 2.0, Google's standard protocol for accessing user data. When a teacher signs in, they're redirected to Google's consent screen, grant permission for specific Classroom scopes, and return to your app with an authorization code. Your server exchanges this code for tokens, which are then used to call the Classroom API on their behalf.

Important: some Classroom API scopes are restricted and require your Google Cloud project to go through Google's OAuth verification process. Scopes for viewing courses and coursework you own work with any Google account. Scopes that access other people's data (student rosters, all submissions in a course) require Google Workspace for Education accounts and may trigger Google's OAuth app review process. This tutorial covers the common teacher-facing scopes that work without verification.

## Before you start

- A Bolt.new account with a Next.js project created
- A Google account with access to Google Cloud Console (console.cloud.google.com)
- A Google Classroom account with at least one course (for testing)
- A deployed Bolt Cloud or Netlify URL to register as the OAuth redirect URI
- Basic understanding of OAuth 2.0 authorization code flow

## Step-by-step guide

### 1. Enable Google Classroom API and Create OAuth 2.0 Credentials

Start at Google Cloud Console (console.cloud.google.com). Create a new project or select an existing one. Navigate to APIs & Services → Library, search for 'Google Classroom API', and click Enable. You'll also need to enable the 'Google People API' if you plan to access student profile information.

Next, configure the OAuth consent screen: go to APIs & Services → OAuth consent screen. Choose 'External' if you want any Google account to log in (required for testing), or 'Internal' if you're building for a specific Google Workspace for Education domain. Fill in the app name, support email, and developer contact. On the Scopes step, add the scopes you need: start with '.../auth/classroom.courses.readonly' for listing courses and '.../auth/classroom.coursework.me.readonly' for viewing assignments. Do NOT add student-roster or submission-reading scopes unless you need them — they trigger a full Google OAuth verification review.

Now create credentials: APIs & Services → Credentials → Create Credentials → OAuth Client ID. Select 'Web application'. Under 'Authorized redirect URIs', add your deployed URL (e.g., https://your-app.netlify.app/api/auth/callback/google). You'll update this after deployment if you don't have a URL yet. Download the credentials JSON — you'll need the client_id and client_secret.

**Expected result:** Google Classroom API is enabled in your GCP project. OAuth consent screen is configured. You have a client_id and client_secret from the downloaded credentials JSON.

### 2. Configure Environment Variables and Install Dependencies

In your Bolt.new project, add the OAuth credentials and configuration to your .env file. You'll need the Google client ID and secret (server-side only — no NEXT_PUBLIC_ prefix), the OAuth callback URL (your deployed domain), and a session secret for encrypting tokens stored in cookies.

For the Google Classroom API calls, you'll use the googleapis npm package, which provides typed clients for all Google APIs including Classroom. For session management, iron-session provides encrypted, stateless cookie sessions that work well with Next.js API routes and don't require a database to store tokens.

Prompt Bolt to install these packages and set up the basic project structure.

```
# .env file additions
# OAuth credentials — server-side only, never NEXT_PUBLIC_
GOOGLE_CLIENT_ID=your_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your_client_secret
# Update this to your deployed URL before testing OAuth
GOOGLE_REDIRECT_URI=https://your-app.netlify.app/api/auth/callback
# Generate a random 32+ character string for session encryption
SESSION_SECRET=replace_with_32_character_random_string_here
# OAuth scopes — space-separated
GOOGLE_SCOPES=https://www.googleapis.com/auth/classroom.courses.readonly https://www.googleapis.com/auth/classroom.coursework.me.readonly
```

**Expected result:** Dependencies installed, .env file configured, and lib/ files created without TypeScript errors.

### 3. Build OAuth 2.0 Authorization and Callback API Routes

The OAuth flow requires two API routes: one that redirects users to Google's authorization page, and one that handles the callback with the authorization code and exchanges it for tokens.

The authorization route generates the Google OAuth URL with your configured scopes, sets the access_type to 'offline' (required to receive a refresh token for long-lived access), and includes 'prompt: consent' to ensure you get a refresh token even if the user has previously authorized. Without 'prompt: consent', Google only sends a refresh token on the first authorization.

The callback route receives the authorization code from Google, exchanges it for access and refresh tokens, stores them in an encrypted session cookie, and redirects the user to the dashboard. Token storage in a session cookie means no database is required for basic use — but if you need tokens to persist across sessions or for server-to-server calls without a logged-in user, store them in your database instead.

```
// app/api/auth/login/route.ts
import { NextResponse } from 'next/server';
import { google } from 'googleapis';

function getOAuthClient() {
  return new google.auth.OAuth2(
    process.env.GOOGLE_CLIENT_ID,
    process.env.GOOGLE_CLIENT_SECRET,
    process.env.GOOGLE_REDIRECT_URI
  );
}

export async function GET() {
  const oauth2Client = getOAuthClient();
  const scopes = (process.env.GOOGLE_SCOPES ?? '').split(' ');

  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: scopes,
    prompt: 'consent', // Always get refresh_token
  });

  return NextResponse.redirect(authUrl);
}

// app/api/auth/callback/route.ts
import { NextResponse } from 'next/server';
import { google } from 'googleapis';

export async function GET(request: Request) {
  const url = new URL(request.url);
  const code = url.searchParams.get('code');
  const error = url.searchParams.get('error');

  if (error || !code) {
    return NextResponse.redirect(new URL('/?error=auth_failed', request.url));
  }

  try {
    const oauth2Client = new google.auth.OAuth2(
      process.env.GOOGLE_CLIENT_ID,
      process.env.GOOGLE_CLIENT_SECRET,
      process.env.GOOGLE_REDIRECT_URI
    );

    const { tokens } = await oauth2Client.getToken(code);
    // Store tokens in session cookie (implement with iron-session)
    // tokens.access_token, tokens.refresh_token, tokens.expiry_date
    
    return NextResponse.redirect(new URL('/dashboard', request.url));
  } catch (err) {
    console.error('OAuth callback error:', err);
    return NextResponse.redirect(new URL('/?error=token_exchange_failed', request.url));
  }
}
```

**Expected result:** OAuth routes are created. Visiting /api/auth/login redirects to Google's consent screen. After authorizing, Google redirects to /api/auth/callback with a code parameter.

### 4. Fetch and Display Google Classroom Data

With tokens stored in the session, you can now make authenticated Classroom API calls. Create an API route that fetches the user's courses and coursework, then build a React component that displays this data.

The googleapis package provides a typed classroom client. You initialize it with the OAuth2 client that has tokens set, then call classroom.courses.list() to get all courses, classroom.courses.courseWork.list() to get assignments in a specific course, and classroom.courses.courseWork.studentSubmissions.list() to get submission data.

For rate limits, the Classroom API allows 500 requests per 100 seconds per project and 100 requests per 100 seconds per user. For a dashboard that loads multiple courses' assignments at once, use sequential requests with small delays rather than Promise.all() on many simultaneous calls.

```
// app/api/classroom/courses/route.ts
import { NextResponse } from 'next/server';
import { google } from 'googleapis';

export async function GET(request: Request) {
  // Get tokens from session cookie
  // (Replace with your actual session retrieval logic)
  const accessToken = 'YOUR_ACCESS_TOKEN_FROM_SESSION';
  const refreshToken = 'YOUR_REFRESH_TOKEN_FROM_SESSION';

  try {
    const oauth2Client = new google.auth.OAuth2(
      process.env.GOOGLE_CLIENT_ID,
      process.env.GOOGLE_CLIENT_SECRET,
      process.env.GOOGLE_REDIRECT_URI
    );
    oauth2Client.setCredentials({
      access_token: accessToken,
      refresh_token: refreshToken,
    });

    const classroom = google.classroom({ version: 'v1', auth: oauth2Client });

    const coursesResponse = await classroom.courses.list({
      courseStates: ['ACTIVE'],
      pageSize: 20,
    });

    const courses = coursesResponse.data.courses ?? [];
    return NextResponse.json({ courses });
  } catch (error: unknown) {
    if ((error as { code?: number }).code === 401) {
      return NextResponse.json({ error: 'Token expired — please re-authenticate' }, { status: 401 });
    }
    console.error('Classroom API error:', error);
    return NextResponse.json({ error: 'Failed to fetch courses' }, { status: 500 });
  }
}
```

**Expected result:** The /api/classroom/courses endpoint returns a JSON array of the authenticated user's active courses. The dashboard component displays course cards correctly.

### 5. Deploy and Complete OAuth Testing

Since OAuth callbacks require a registered redirect URI, you must deploy before the complete authentication flow works. The Bolt preview URL (containing webcontainer-api.io) cannot be registered as an authorized URI in Google Cloud Console.

Deploy to Netlify by connecting your GitHub repository in Netlify's dashboard and adding all environment variables: GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI (set to your Netlify URL + /api/auth/callback), and SESSION_SECRET. After your first deploy, you'll know your Netlify URL — go back to Google Cloud Console → APIs & Services → Credentials → your OAuth client → edit → add the new redirect URI.

Once deployed, test the flow by visiting your Netlify URL, clicking login, authenticating with a Google account that has Classroom access, and verifying that courses appear in the dashboard. Check the browser's Network tab to confirm API calls return 200 responses with real course data.

**Expected result:** Visiting the deployed Netlify URL, clicking 'Sign in with Google', and completing the OAuth flow successfully redirects to the dashboard showing real Google Classroom courses.

## Best practices

- Request only the minimum OAuth scopes needed — start with courses.readonly and coursework.me.readonly, adding broader scopes only when required
- Always set access_type: 'offline' and prompt: 'consent' in the OAuth URL to ensure you receive a refresh token for long-lived access
- Store refresh tokens securely — they are long-lived credentials that can be used to generate access tokens indefinitely until revoked
- Implement a token refresh listener to automatically save new access tokens when the OAuth client refreshes them
- Test on a deployed domain (not Bolt's WebContainer preview) since OAuth callbacks require a stable, registered redirect URI
- Handle 401 errors from the Classroom API by redirecting users to re-authenticate — access tokens expire and refresh tokens can be revoked
- Cache course and coursework data for a few minutes to avoid hitting Classroom API rate limits (500 requests/100 seconds per project)
- Be transparent with users about what data your app accesses — the OAuth consent screen lists all scopes, but a clear in-app explanation builds trust

## Use cases

### Teacher Assignment Dashboard

Build a consolidated dashboard showing all active assignments across multiple Google Classroom courses. Teachers can see upcoming due dates, submission counts, and grading status without switching between individual classes — useful for teachers managing 5+ sections simultaneously.

Prompt example:

```
Build a teacher dashboard that connects to the Google Classroom API. After OAuth login, display all the teacher's courses in a grid. When clicking a course, show the list of coursework (assignments and questions) with due dates and the number of submissions turned in. Use Next.js API routes for the OAuth flow and Classroom API calls, and store the refresh token in an encrypted cookie.
```

### Student Progress Tracker

Create a student-facing app that shows all their courses, pending assignments, and submitted work in a timeline view. Students can see what's due this week across all classes in one place, reducing missed assignments from notification overload in the Classroom interface.

Prompt example:

```
Create a student assignment tracker using the Google Classroom API. After the student authenticates with their Google account, fetch all their enrolled courses and the coursework in each course. Show assignments grouped by due date with a 'submitted' or 'pending' badge. Filter to show only assignments due in the next 7 days by default.
```

### Parent Portal for Course Visibility

Build a read-only portal where parents can view their child's course list and upcoming assignments without logging into the student's account. This requires the student to authenticate once and the app to store their tokens securely, displaying sanitized assignment information.

Prompt example:

```
Build a parent portal where students can authenticate with Google Classroom, and the app saves their course and assignment data to a database. Parents receive a share link to view upcoming assignments without needing Google accounts. Include an API route that fetches fresh data from Classroom every hour using the stored refresh token.
```

## Troubleshooting

### Google OAuth shows 'redirect_uri_mismatch' error after authentication

Cause: The redirect URI registered in Google Cloud Console does not exactly match the URL your app sends in the OAuth request. Even minor differences (trailing slash, http vs https, different path) cause this error.

Solution: Go to Google Cloud Console → APIs & Services → Credentials → your OAuth client. Verify the exact URI in Authorized redirect URIs matches process.env.GOOGLE_REDIRECT_URI exactly, including protocol, domain, and path. Ensure you're testing on the deployed URL, not the Bolt preview URL.

### OAuth flow works but tokens expire and API calls start returning 401

Cause: Access tokens expire after 1 hour. Without a refresh token flow implemented, expired tokens are not automatically renewed.

Solution: Implement a token refresh listener on the OAuth2 client. When the client automatically refreshes the access token, save the new token to your session or database.

```
oauth2Client.on('tokens', (tokens) => {
  if (tokens.access_token) {
    // Save updated access_token to session or database
    console.log('Token refreshed:', tokens.access_token);
  }
});
```

### 'Request had insufficient authentication scopes' when calling courses.courseWork.list()

Cause: The classroom.coursework.me.readonly scope only allows reading the authenticated user's own assignments. If you're trying to read all assignments in a course as a teacher, you need the classroom.coursework.readonly scope.

Solution: Add the classroom.coursework.readonly scope to GOOGLE_SCOPES in your .env file and update the authorized scopes in Google Cloud Console. Users will need to re-authorize to grant the new scope.

```
GOOGLE_SCOPES=https://www.googleapis.com/auth/classroom.courses.readonly https://www.googleapis.com/auth/classroom.coursework.readonly
```

### API calls fail with 'The caller does not have permission' for student roster or submission endpoints

Cause: Scopes like classroom.rosters and accessing other users' submissions require Google Workspace for Education accounts. Personal Gmail accounts cannot access these scopes.

Solution: Test with a Google Workspace for Education account (usually a school-issued email). If your use case requires these scopes with personal accounts, apply for Google's OAuth verification process, which involves a security assessment.

## Frequently asked questions

### Can I test the Google Classroom integration in Bolt's preview window?

You can develop and test the API route code structure in Bolt's preview, but the OAuth authentication flow requires deployment. Google's OAuth callback requires a registered, stable redirect URI, and Bolt's WebContainer uses dynamic preview URLs that can't be registered. Deploy to Netlify or Bolt Cloud first, register your deployed URL in Google Cloud Console, and test the complete flow there.

### Does the Google Classroom API work with personal Gmail accounts or only Google Workspace for Education?

Basic scopes for reading your own courses and assignments work with any Google account, including personal Gmail. However, scopes that access other users' data (student rosters, all submissions in a course, teacher-level access) require Google Workspace for Education accounts. If you need these scopes with personal accounts, Google's OAuth app verification process is required.

### How do I connect Bolt.new to Google Classroom?

Enable the Google Classroom API in Google Cloud Console, create OAuth 2.0 Web Application credentials, configure the redirect URI to your deployed app URL, store the client_id and client_secret in your .env file, and build Next.js API routes for the OAuth authorization and callback flow. The googleapis npm package provides a typed Classroom client for making API calls after authentication.

### What data can I read from the Google Classroom API?

With the classroom.courses.readonly scope you can list courses and their details. With classroom.coursework.me.readonly you can read assignments and questions. With classroom.student-submissions.me.readonly you can read submission status for the authenticated user. Teacher-level access to student rosters and all submissions requires Google Workspace for Education accounts and broader scopes that may need OAuth verification.

### Is there a Google Classroom API free tier?

The Google Classroom API is free to use with no per-call charges. It's subject to usage quotas: 500 requests per 100 seconds per project and 100 requests per 100 seconds per user. These limits are generous for most applications. If you need higher quotas, you can request increases in Google Cloud Console.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/google-classroom
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/google-classroom
