Building a document OCR tool using GCP OCR and Node.js
Optical Character Recognition (OCR) unlocks text content within images and PDFs, enabling features like searchable documents, automated data entry, and content analysis. In this DevTip, we'll build a document OCR tool using GCP OCR and Node.js to efficiently extract text from images and PDFs in your applications.
Introduction
GCP OCR, powered by the Google Cloud Vision API, provides robust image analysis capabilities, including OCR for text extraction. Integrating this service into your Node.js application allows you to process images and PDFs programmatically and extract text data efficiently.
This guide walks you through setting up the Google Cloud Vision API, authenticating your application, and writing Node.js code to perform OCR on images and PDFs.
Prerequisites
Ensure you have the following:
- A Google Cloud Platform (GCP) account
- Node.js version 22 or later installed
- Basic knowledge of JavaScript and Node.js
Setting up Google Cloud Vision API
1. Create a GCP project
- Go to the Google Cloud Console.
- Click on the project dropdown and select New Project.
- Enter a project name and click Create.
- Enable billing for the project.
2. Enable the Vision API
- In the Cloud Console, navigate to APIs & Services > Library.
- Search for Cloud Vision API.
- Click on Cloud Vision API and then click Enable.
Authentication setup
-
Create a service account key:
- Go to IAM & Admin > Service Accounts
- Create a new service account or select an existing one
- Create a new key (JSON format)
- Download and securely store the JSON key file
-
Set up authentication in your application:
const vision = require('@google-cloud/vision') const client = new vision.ImageAnnotatorClient({ keyFilename: 'path/to/your/service-account-key.json', })Or use environment variables:
export GOOGLE_APPLICATION_CREDENTIALS="path/to/your/service-account-key.json"
Keep key files outside your repository. The examples below use Application Default Credentials,
which can also use an attached service account in Google Cloud or local credentials configured
with gcloud auth application-default login.
Installing the Google Cloud Vision client library
Initialize a new Node.js project and install the necessary library:
mkdir ocr-project
cd ocr-project
npm init -y
npm install @google-cloud/vision@4 @google-cloud/storage@7
Writing the Node.js code
Create an index.js file in your project directory. This CommonJS example processes individual
images; use the separate PDF/TIFF file API below for multipage documents:
// index.js
const vision = require('@google-cloud/vision');
// Creates a client
const client = new vision.ImageAnnotatorClient();
async function extractTextFromImage(imagePath) {
try {
const [result] = await client.textDetection(imagePath);
if (result.error?.code) {
throw Object.assign(new Error('Vision text detection failed', { cause: result.error }), {
code: result.error.code,
});
}
const detections = result.textAnnotations ?? [];
return detections.map(text => text.description);
} catch (error) {
if (error.code === 8) {
console.error('API quota exceeded');
} else if (error.code === 7) {
console.error('Permission denied: check IAM permissions and API enablement');
}
throw error;
}
}
// Example usage
extractTextFromImage('images/sample.jpg')
.then(text => console.log('Extracted text:', text))
.catch(error => {
console.error('OCR failed:', error.message);
process.exitCode = 1;
});
Processing PDF files
To extract text from PDFs, use the asynchronous batch annotation feature with Google Cloud Storage.
Upload the PDF to a private bucket first. The calling identity needs permission to read the input,
create output objects, and list and read those outputs. Supply a new, empty output prefix ending in
/ for each job so results cannot mix with older jobs. Reuse client from above:
const { Storage } = require('@google-cloud/storage');
const storage = new Storage();
async function extractTextFromPDF(gcsSourceUri, gcsDestinationUri) {
const destination = /^gs:\/\/([^/]+)\/(.+\/)$/u.exec(gcsDestinationUri);
if (!destination) {
throw new Error('Use a gs://bucket/unique-output-prefix/ destination');
}
const [, bucketName, prefix] = destination;
const inputConfig = {
mimeType: 'application/pdf',
gcsSource: {
uri: gcsSourceUri
}
};
const outputConfig = {
gcsDestination: {
uri: gcsDestinationUri
},
batchSize: 1
};
const features = [{ type: 'DOCUMENT_TEXT_DETECTION' }];
const request = {
requests: [{
inputConfig,
features,
outputConfig,
}]
};
const [operation] = await client.asyncBatchAnnotateFiles(request);
await operation.promise();
// The operation returns output locations; OCR text lives in the JSON objects in Storage.
const [files] = await storage.bucket(bucketName).getFiles({ prefix });
const pages = [];
for (const file of files.filter(file => file.name.endsWith('.json'))) {
const [contents] = await file.download();
const result = JSON.parse(contents.toString('utf8'));
if (result.error?.code) {
throw Object.assign(new Error('PDF file failed', { cause: result.error }), {
code: result.error.code,
});
}
for (const page of result.responses ?? []) {
if (page.error?.code) {
throw Object.assign(new Error('PDF page failed', { cause: page.error }), {
code: page.error.code,
});
}
if (!Number.isInteger(page.context?.pageNumber)) {
throw new Error('PDF result is missing its page number');
}
pages.push({ number: page.context.pageNumber, text: page.fullTextAnnotation?.text ?? '' });
}
}
if (pages.length === 0) throw new Error('Vision returned no PDF pages');
return pages.sort((a, b) => a.number - b.number).map(page => page.text).join('\n');
}
Call extractTextFromPDF('gs://your-input-bucket/document.pdf', 'gs://your-output-bucket/unique-job-id/') and handle its rejected promise as in the image example.
For TIFF input, use image/tiff as the MIME type. Configure a storage lifecycle policy appropriate
for your documents and OCR output; the example retains both. Close the shared Vision client with
await client.close() after all work finishes in a CLI application.
See Google’s PDF/TIFF OCR guide for the request and output formats.
API limitations and pricing
- PDF processing limit: 2,000 pages per file
- Each PDF/TIFF page counts as an individual image for billing; five pages are five units per feature, even when grouped into one request or output file.
- Check Cloud Vision pricing for current free-tier and volume rates. Cloud Storage charges are separate.
Regional configuration
For data residency requirements, you can specify regional endpoints:
const client = new vision.ImageAnnotatorClient({
apiEndpoint: 'eu-vision.googleapis.com', // European Union
// or 'us-vision.googleapis.com' // United States
})
Troubleshooting
Common issues and solutions
-
Authentication Errors
- Verify the service account key file path
- Ensure the service account has proper permissions
- Check if the API is enabled in your project
-
PDF Processing Issues
- Confirm the PDF file has no more than 2,000 pages
- Verify GCS bucket permissions
- Check PDF file format compatibility
-
Rate Limiting
- Implement exponential backoff for retries
- Monitor quota usage in the GCP Console
- Consider batch processing for large volumes
Best practices
-
Error Handling
async function processDocument(filePath) { try { const result = await extractTextFromImage(filePath); return result; } catch (error) { if (error.code === 'ENOENT') { throw new Error('File not found', { cause: error }); } if (error.code === 8) { throw new Error('API quota exceeded', { cause: error }); } if (error.code === 7) { throw new Error('Permission denied: check IAM permissions and API enablement', { cause: error, }); } throw error; } } -
Batch Processing
async function processBatch(files, concurrency = 3) { if (!Number.isInteger(concurrency) || concurrency < 1) { throw new Error('Concurrency must be a positive integer'); } const results = []; for (let i = 0; i < files.length; i += concurrency) { const batch = files.slice(i, i + concurrency); const batchResults = await Promise.all( batch.map(file => processDocument(file).catch(() => ({ error: 'Image OCR failed' }))) ); results.push(...batchResults); await new Promise(resolve => setTimeout(resolve, 1000)); // Rate limiting } return results; }
Conclusion
Integrating GCP OCR into your Node.js applications enables powerful OCR capabilities. By automating text extraction from images and PDFs, you can enhance your application's functionality, streamline workflows, and provide more value to your users.
Transloadit also offers a Document OCR robot as part of our Artificial Intelligence service for seamless and scalable OCR processing.
