Modern web development demands applications that are not only performant and scalable but also universally accessible. This post guides you through integrating Express, Google Cloud Platform (GCP), and Accessibility (A11y) best practices within a single TypeScript application, ensuring your services are robust, cloud-ready, and inclusive.
Setting Up Express with TypeScript
Express is a minimalist web framework for Node.js, providing a robust set of features for web and mobile applications. TypeScript enhances code quality and maintainability by adding static typing to JavaScript.
First, initialize your project and install dependencies:
npm init -y
npm install express @types/express typescript ts-node ejs
npx tsc --init
Configure tsconfig.json for your project:
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
Create your Express application in src/app.ts:
import express, { Request, Response } from 'express';
import path from 'path';
const app = express();
const port = process.env.PORT || 8080;
// Set EJS as the view engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, '../views'));
// Serve static files (e.g., CSS, JS) from a 'public' directory
app.use(express.static(path.join(__dirname, '../public')));
app.get('/', (req: Request, res: Response) => {
// Render the 'index' view with some data
res.render('index', {
pageTitle: 'Welcome to Our Accessible App',
mainContent: 'This is the main content of our accessible application.',
imageUrl: '/images/hero.jpg',
imageAlt: 'A beautiful landscape scene representing tranquility'
});
});
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
Integrating Google Cloud Platform (GCP)
Google Cloud Platform offers a suite of cloud computing services. We'll leverage Cloud Run for serverless deployment and demonstrate integration with Cloud Storage for serving content, a common pattern for dynamic applications.
Install the Cloud Storage client library:
npm install @google-cloud/storage
Create a service to interact with Cloud Storage (src/services/gcpStorage.ts):
import { Storage } from '@google-cloud/storage';
const storage = new Storage();
const bucketName = process.env.GCS_BUCKET_NAME || 'your-gcp-bucket-name'; // Replace with your bucket name
export async function getFileContent(fileName: string): Promise<string | null> {
try {
const file = storage.bucket(bucketName).file(fileName);
const [exists] = await file.exists();
if (!exists) {
console.warn(`File ${fileName} not found in bucket ${bucketName}`);
return null;
}
const [content] = await file.download();
return content.toString('utf8');
} catch (error) {
console.error(`Error fetching file ${fileName} from GCS:`, error);
return null;
}
}
// Example usage in app.ts (simplified for brevity, integrate into a route handler)
/*
import { getFileContent } from './services/gcpStorage';
app.get('/about', async (req: Request, res: Response) => {
const aboutContent = await getFileContent('about.html');
res.render('about', { aboutContent });
});
*/
This getFileContent function allows your Express app to dynamically fetch content or assets from a Cloud Storage bucket, which can be useful for managing textual content, images, or other media external to your application's deployment bundle.
Crafting Accessible Views (A11y)
Accessibility ensures that people with disabilities can perceive, understand, navigate, and interact with your web application. Server-side rendering with semantic HTML is foundational for A11y.
Create views/index.ejs with key A11y considerations:
<!DOCTYPE html>
<html lang="en"> <!-- Always specify the language -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= pageTitle %> - My Accessible App</title> <!-- Descriptive page title -->
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<a href="#main-content" class="skip-link">Skip to main content</a> <!-- Skip link for keyboard users -->
<header>
<nav aria-label="Main navigation"> <!-- Semantic navigation with ARIA label -->
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
</header>
<main id="main-content"> <!-- Main content landmark -->
<h1><%= pageTitle %></h1>
<p><%= mainContent %></p>
<figure>
<img src="<%= imageUrl %>" alt="<%= imageAlt %>"> <!-- Crucial alt text for images -->
<figcaption>A tranquil scene to welcome you.</figcaption>
</figure>
<section aria-labelledby="form-heading"> <!-- ARIA label for sections -->
<h2 id="form-heading">Provide Feedback</h2>
<form action="/submit-feedback" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name" autocomplete="name" required aria-required="true">
<label for="email">Email:</label>
<input type="email" id="email" name="email" autocomplete="email" required aria-required="true">
<label for="message">Message:</label>
<textarea id="message" name="message" rows="5" required aria-required="true"></textarea>
<button type="submit">Send Feedback</button>
</form>
<div id="status-message" role="status" aria-live="polite"></div> <!-- ARIA live region for dynamic updates -->
</section>
</main>
<footer>
<p>© 2025 Accessible Cloud App. All rights reserved.</p>
</footer>
</body>
</html>
Key A11y points demonstrated:
langattribute on<html>: Specifies the page's primary language for screen readers.- Skip Link: Allows keyboard users to bypass repetitive navigation.
- Semantic HTML5: Using
<header>,<nav>,<main>,<footer>,<section>,<figure>for clear document structure. altattributes: Essential for images, providing textual descriptions for visually impaired users.- Form
labelandfor: Associates labels with form controls, improving usability for all. autocomplete: Helps users fill out forms faster.aria-label/aria-labelledby: Provides additional context for elements when visual cues aren't enough.aria-live="polite": Informs screen readers of important, non-urgent updates to a region (e.g., form submission status).
Deployment on Cloud Run
Cloud Run is a fully managed serverless platform for containerized applications. It automatically scales your Express app from zero to many instances based on demand.
Create a Dockerfile in your project root:
# Use the official Node.js 20 image
FROM node:20-slim
# Set the working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install --omit=dev
# Copy source code and compiled JavaScript
COPY . .
RUN npm run build # Assuming you have a 'build' script in package.json to compile TS to JS
# Expose the port the app runs on
EXPOSE 8080
# Run the application
CMD ["node", "dist/app.js"]
Add a build script to your package.json:
"scripts": {
"start": "ts-node src/app.ts",
"build": "tsc",
"serve": "node dist/app.js"
},
Deploy to Cloud Run using the gcloud CLI:
gcloud run deploy my-accessible-app \
--source . \
--region us-central1 \
--platform managed \
--allow-unauthenticated
This command builds your Docker image, pushes it to Google Container Registry, and deploys it as a new Cloud Run service, making your accessible Express application available globally.
Conclusion
Integrating Express, GCP, and Accessibility with TypeScript creates a powerful, scalable, and inclusive foundation for your web applications. By adopting these practices from the outset, you build services that cater to a wider audience while leveraging the benefits of modern cloud infrastructure. Prioritizing accessibility isn't just a best practice; it's a commitment to building a better web for everyone.