This guide explains how the frontend, backend, and smart contract work together in the Web3 Student Lab platform.
┌─────────────┐ HTTP/REST ┌─────────────┐ RPC Calls ┌──────────────┐
│ Frontend │ ◄─────────────────► │ Backend │ ◄─────────────────► │ Soroban │
│ (Next.js) │ JSON + JWT │ (Node.js) │ Transactions │ (Stellar) │
└─────────────┘ └─────────────┘ └──────────────┘
│ │ │
│ │ │
▼ ▼ ▼
Browser/Client PostgreSQL DB Blockchain State
- Authentication - Users - Certificates
- UI Rendering - Courses - Immutable Records
- Blockchain Verification - Enrollments - Verification
- Certificates
API Base URL: http://localhost:8080/api
All API communication happens through the typed client in /frontend/src/lib/api.ts.
// Frontend: User login
const { user, token } = await authAPI.login({
email: 'student@example.com',
password: 'password123',
});
// Token is automatically stored in localStorage
// and attached to subsequent requests
// Protected route example
const courses = await coursesAPI.getAll();
// Header: Authorization: Bearer <token>| Endpoint | Method | Frontend Function | Description |
|---|---|---|---|
/auth/register |
POST | authAPI.register() |
Register new student |
/auth/login |
POST | authAPI.login() |
Student login |
/auth/me |
GET | authAPI.getCurrentUser() |
Get current user |
/courses |
GET | coursesAPI.getAll() |
List all courses |
/courses/:id |
GET | coursesAPI.getById() |
Get course details |
/enrollments |
POST | enrollmentsAPI.enroll() |
Enroll in course |
/certificates/student/:id |
GET | certificatesAPI.getByStudentId() |
Get student certificates |
/verify |
GET | verifyCertificateOnChain() |
Verify on blockchain |
The backend uses Prisma ORM to interact with PostgreSQL.
Schema Models:
Student- User accountsCourse- Available coursesEnrollment- Course enrollmentsCertificate- Issued certificatesFeedback- Course reviews
Database Connection:
// Backend: src/db/index.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default prisma;The backend integrates with Soroban contracts for certificate issuance and verification.
Contract Functions:
issue(symbol, student, course_name)- Issue certificate on-chainget_certificate(symbol)- Retrieve certificate from blockchain
Integration Point:
// Backend: When issuing a certificate
await prisma.certificate.create({
data: {
studentId,
courseId,
status: 'pending', // Will be updated after on-chain issuance
},
});
// Then call Soroban contract
const txHash = await issueCertificateOnChain(symbol, student, courseName);The frontend can directly verify certificates on the blockchain without going through the backend.
// Frontend: Verify certificate
import { verifyCertificateOnChain } from '@/lib/soroban';
const certData = await verifyCertificateOnChain('CERTIFICATE_SYMBOL');
// Returns: { symbol, student, course_name, issue_date }-
Frontend: Student browses courses (
/courses)const courses = await coursesAPI.getAll();
-
Backend: Fetches courses from database
const courses = await prisma.course.findMany();
-
Frontend: Student clicks "Enroll" on course detail page
await enrollmentsAPI.enroll(user.id, course.id);
-
Backend: Creates enrollment record
const enrollment = await prisma.enrollment.create({ data: { studentId, courseId, status: 'active' }, });
-
Frontend: Updates UI to show enrollment success
-
Backend: Student completes course
// Create certificate in database const cert = await prisma.certificate.create({ data: { studentId, courseId, status: 'pending', }, });
-
Backend: Issue on Soroban blockchain
const symbol = `CERT-${course.code}-${student.id}`; await sorobanClient.issue(symbol, student.name, course.title); // Update certificate with on-chain hash await prisma.certificate.update({ where: { id: cert.id }, data: { certificateHash: txHash, status: 'issued', }, });
-
Frontend: Display certificate to student
const certificates = await certificatesAPI.getByStudentId(user.id);
-
Anyone: Verify certificate authenticity
const cert = await verifyCertificateOnChain('CERT-SYMBOL'); console.log('Verified:', cert.student === 'Expected Student');
# Backend API
NEXT_PUBLIC_API_URL=http://localhost:8080/api
# Soroban/Stellar
NEXT_PUBLIC_SOROBAN_RPC_URL=https://soroban-test.stellar.org:443
NEXT_PUBLIC_CERTIFICATE_CONTRACT_ID=CD... # Deployed contract IDPORT=8080
DATABASE_URL="postgresql://user:pass@localhost:5432/web3lab"
JWT_SECRET=your-secret-key-change-in-production
NODE_ENV=development
# Soroban Configuration
SOROBAN_RPC_URL=https://soroban-test.stellar.org:443
SOROBAN_NETWORK_PASSPHRASE=Test SDF Network ; September 2015
CONTRACT_ADMIN_SECRET=SA... # Admin wallet secret keycd backend
npx prisma migrate dev
npx prisma generatecd backend
npm run dev
# Server runs on http://localhost:8080cd frontend
npm install
cp .env.local.example .env.local
# Edit .env.local with correct values
npm run dev
# Frontend runs on http://localhost:3000cd contracts
# Build contract
cargo build --target wasm32-unknown-unknown --release
# Deploy to Stellar testnet
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/certificate_contract.wasm \
--source admin-account# Register new user
curl -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "password123",
"firstName": "Test",
"lastName": "User"
}'
# Login
curl -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "password123"
}'# Get courses
curl http://localhost:8080/api/courses
# Enroll in course (requires auth token)
curl -X POST http://localhost:8080/api/enrollments \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"studentId": "student-id",
"courseId": "course-id"
}'Problem: CORS errors or connection refused
Solutions:
- Ensure backend is running:
curl http://localhost:8080/health - Check
NEXT_PUBLIC_API_URLin frontend.env.local - Verify backend CORS configuration allows
http://localhost:3000
Problem: 401 Unauthorized errors
Solutions:
- Clear browser localStorage
- Re-login to get fresh token
- Check JWT_SECRET matches in backend
- Verify token expiration time
Problem: Can't verify certificates on-chain
Solutions:
- Check
NEXT_PUBLIC_CERTIFICATE_CONTRACT_IDis set - Verify Soroban RPC endpoint is accessible
- Ensure contract is deployed to the network
- Check network passphrase matches (testnet vs mainnet)
- ✅ JWT tokens stored in localStorage (consider httpOnly cookies for production)
- ✅ HTTPS required in production
- ✅ Input validation on all forms
- ✅ XSS protection via React's automatic escaping
⚠️ Implement CSRF protection for production
- ✅ Password hashing with bcrypt
- ✅ JWT token authentication
- ✅ SQL injection prevention via Prisma
- ✅ CORS configuration
- ✅ Rate limiting (implement for production)
⚠️ Add request validation middleware
- ✅ Immutable certificate records
- ✅ Cryptographic verification
⚠️ Secure admin key management⚠️ Implement access controls for certificate issuance⚠️ Multi-sig for production contract administration
- Static generation for landing pages
- Incremental static regeneration for course listings
- Client-side caching of API responses
- Lazy loading of heavy components
- Database indexing on frequently queried fields
- Connection pooling via Prisma
- Response caching for public endpoints
- Redis for session management (production)
- Batch certificate issuance for multiple students
- Optimize contract storage usage
- Use Soroban's simulation for gas estimation
// Add error tracking
try {
await apiCall();
} catch (error) {
console.error('API Error:', error);
// Send to error tracking service (e.g., Sentry)
}// Request logging middleware
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
next();
});- Deploy Contract: Deploy Soroban contract to Stellar testnet
- Seed Database: Add sample courses and users
- Test Flows: Complete end-to-end testing of all user flows
- Production Setup: Configure production environment variables
- Monitoring: Set up error tracking and analytics
This integration ensures seamless communication between all three layers while maintaining security, performance, and scalability.