This is a Next.js project bootstrapped with create-next-app.
First, start the Typesense server. In this repository you'll find a docker container and a script run-typesense-server.sh that runs the container and loads the news dataset from Huggingface: https://github.com/destaquesgovbr/typesense
Copy the example environment file:
cp .env.example .env.localThe Typesense container fetches its API key from GCP Secret Manager on startup. Get the actual API key from the container logs:
docker logs govbrnews-typesense | grep "API Key:"Update your .env.local file with the API key:
NEXT_PUBLIC_TYPESENSE_HOST=localhost
NEXT_PUBLIC_TYPESENSE_SEARCH_ONLY_API_KEY=<your-api-key-from-logs>pnpm devOpen http://localhost:3000 with your browser to see the result.
This project uses Vitest for unit/integration tests and Playwright for E2E tests.
# Run unit tests in watch mode
npm test
# Run unit tests once
npm run test:unit
# Run tests with coverage report
npm run test:coverage
# Open Vitest UI
npm run test:ui
# Run E2E tests
npm run test:e2e
# Open Playwright UI
npm run test:e2e:uisrc/
├── __tests__/ # Test utilities and mocks
│ ├── setup.ts # Global test setup
│ ├── test-utils.tsx # Custom render with providers
│ └── mocks/
│ └── fixtures/ # Test data fixtures
├── lib/__tests__/ # Unit tests for lib/
│ ├── result.test.ts # Result type tests
│ └── utils.test.ts # Utility function tests
└── config/__tests__/ # Unit tests for config/
└── prioritization.test.ts
e2e/ # Playwright E2E tests
Unit tests use Vitest with React Testing Library:
import { describe, expect, it } from 'vitest'
import { render, screen } from '@/__tests__/test-utils'
import MyComponent from './MyComponent'
describe('MyComponent', () => {
it('renders correctly', () => {
render(<MyComponent />)
expect(screen.getByText('Hello')).toBeInTheDocument()
})
})E2E tests use Playwright against the production site:
import { test, expect } from '@playwright/test'
test('home page loads', async ({ page }) => {
await page.goto('/')
await expect(page.getByRole('heading')).toBeVisible()
})Tests run automatically on every PR via GitHub Actions (.github/workflows/test.yml).
If you see errors like:
RequestUnauthorized: Request failed with HTTP code 401 | Server said: Forbidden - a valid `x-typesense-api-key` header must be sent.
Solution:
-
Check if the Typesense container is running:
docker ps | grep typesense -
Get the actual API key from container logs:
docker logs govbrnews-typesense | grep "API Key:"
-
Update your
.env.localfile with the correct API key -
Restart the development server:
pnpm dev
Check if Typesense is running:
curl http://localhost:8108/healthExpected response: {"ok":true}
Verify API key works:
curl -H "X-TYPESENSE-API-KEY: your-api-key" http://localhost:8108/collectionsTo learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.
Check out our Next.js deployment documentation for more details.
This project is deployed to Google Cloud Run using GitHub Actions.
┌─────────────────────┐
│ GCP Secret Manager │
│ typesense-search- │
│ only-api-key │
└──────────┬──────────┘
│
┌──────┴───────┐
▼ ▼
┌─────────┐ ┌─────────┐
│ GitHub │ │Typesense│
│ Actions │ │ VM │
│Workflow │ │ │
└────┬────┘ └─────────┘
│
▼
┌──────────────┐
│ Cloud Run │
│ Portal │
└──────────────┘
The production deployment fetches the Typesense API key directly from GCP Secret Manager during the Docker build process.
GitHub Secrets Required:
GCP_WORKLOAD_IDENTITY_PROVIDER- Workload Identity Federation providerGCP_SERVICE_ACCOUNT- GitHub Actions service account emailNEXT_PUBLIC_TYPESENSE_HOST- Typesense server IP address
GCP Secret Manager:
typesense-search-only-api-key- Search API key (read-only)
Why this architecture?
- Single source of truth: API key is only maintained in GCP Secret Manager
- Automatic sync: Portal always uses the same key as the Typesense VM
- Easy rotation: Update key in GCP → rebuild portal → done
- Audit trail: All secret access is logged in GCP
When code is pushed to main:
- GitHub Actions authenticates to GCP via Workload Identity Federation
- Fetches
typesense-search-only-api-keyfrom Secret Manager - Builds Docker image with API key as build argument
- Pushes image to Artifact Registry
- Deploys to Cloud Run
See .github/workflows/deploy-production.yml for details.
The infrastructure (Typesense VM, secrets, IAM bindings) is managed via Terraform in the infra repository.