Documentation
API Example Code
Complete example code and usage scenarios for SmartCV API, including examples in JavaScript, React, Node.js and other languages
Updated: 12/30/2024
This page provides complete example code for SmartCV API, covering common usage scenarios and best practices.
🔑 Basic Setup
Environment Configuration
# Install dependencies
npm install axios @types/node dotenv
# Environment variables (.env)
NEXT_PUBLIC_API_BASE_URL=https://smartcv.cc/api
NEXTAUTH_SECRET=your-secret-keyAPI Client Initialization
// utils/api-client.js
import axios from 'axios'
const apiClient = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
// Request interceptor - add authentication token
apiClient.interceptors.request.use((config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// Response interceptor - error handling
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Token expired, redirect to login page
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export default apiClient🔐 Authentication Examples
User Registration
// services/auth.js
import apiClient from '@/utils/api-client'
export async function registerUser(userData) {
try {
const response = await apiClient.post('/register', {
name: userData.name,
email: userData.email,
password: userData.password
})
return {
success: true,
data: response.data,
message: 'Registration successful, please check your verification email'
}
} catch (error) {
return {
success: false,
error: error.response?.data?.error || 'Registration failed'
}
}
}
// React component usage
function RegisterForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
password: ''
})
const [loading, setLoading] = useState(false)
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
const result = await registerUser(formData)
if (result.success) {
toast.success(result.message)
router.push('/login')
} else {
toast.error(result.error)
}
setLoading(false)
}
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit" disabled={loading}>
{loading ? 'Registering...' : 'Register'}
</button>
</form>
)
}NextAuth.js Configuration
// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import CredentialsProvider from 'next-auth/providers/credentials'
export default NextAuth({
providers: [
CredentialsProvider({
name: 'credentials',
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' }
},
async authorize(credentials) {
try {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
const user = await response.json()
if (response.ok && user) {
return user
}
return null
} catch (error) {
return null
}
}
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET
})
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id
token.plan = user.plan
}
return token
},
async session({ session, token }) {
session.user.id = token.id
session.user.plan = token.plan
return session
}
}
})📄 Resume Management Examples
Create Resume
// hooks/useResumes.js
import { useState, useCallback } from 'react'
import apiClient from '@/utils/api-client'
export function useResumes() {
const [resumes, setResumes] = useState([])
const [loading, setLoading] = useState(false)
const createResume = useCallback(async (resumeData) => {
setLoading(true)
try {
const response = await apiClient.post('/resumes', {
title: resumeData.title,
content: resumeData.content,
templateId: resumeData.templateId,
personalInfo: resumeData.personalInfo,
sections: resumeData.sections
})
const newResume = response.data.resume
setResumes((prev) => [newResume, ...prev])
return { success: true, resume: newResume }
} catch (error) {
return {
success: false,
error: error.response?.data?.error || 'Failed to create resume'
}
} finally {
setLoading(false)
}
}, [])
const fetchResumes = useCallback(async () => {
setLoading(true)
try {
const response = await apiClient.get('/resumes')
setResumes(response.data.resumes)
} catch (error) {
console.error('Failed to fetch resume list:', error)
} finally {
setLoading(false)
}
}, [])
return {
resumes,
loading,
createResume,
fetchResumes
}
}
// React component usage
function ResumeBuilder() {
const { createResume, loading } = useResumes()
const [resumeData, setResumeData] = useState({
title: '',
templateId: 'modern-template',
personalInfo: {
name: '',
email: '',
phone: '',
location: ''
},
sections: {
summary: { visible: true, content: '' },
experience: { visible: true, items: [] },
education: { visible: true, items: [] },
skills: { visible: true, items: [] }
}
})
const handleSubmit = async () => {
const result = await createResume(resumeData)
if (result.success) {
toast.success('Resume created successfully')
router.push(`/resumes/${result.resume.id}`)
} else {
toast.error(result.error)
}
}
return (
<div className="resume-builder">
{/* Resume editing form */}
<button onClick={handleSubmit} disabled={loading}>
{loading ? 'Creating...' : 'Create Resume'}
</button>
</div>
)
}Resume Export
// services/export.js
export async function exportResumeToPDF(resumeId, options = {}) {
try {
const response = await apiClient.post(
`/resumes/${resumeId}/export/pdf`,
{
format: 'A4',
quality: 'high',
includeWatermark: false,
...options
},
{
responseType: 'blob' // Important: specify response type as blob
}
)
// Create download link
const blob = new Blob([response.data], { type: 'application/pdf' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `resume-${resumeId}.pdf`
// Trigger download
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
// Clean up memory
window.URL.revokeObjectURL(url)
return { success: true }
} catch (error) {
return {
success: false,
error: error.response?.data?.error || 'Export failed'
}
}
}
// React Hook
export function useResumeExport() {
const [exporting, setExporting] = useState(false)
const exportPDF = useCallback(async (resumeId, options) => {
setExporting(true)
const result = await exportResumeToPDF(resumeId, options)
setExporting(false)
return result
}, [])
return { exportPDF, exporting }
}🤖 AI Features Examples
ATS Compatibility Analysis
// services/ai.js
export async function analyzeATSCompatibility(resumeContent, jobDescription) {
try {
const response = await apiClient.post('/ai/analyze', {
type: 'ats_compatibility',
resumeContent,
jobDescription,
options: {
includeKeywords: true,
includeSuggestions: true,
detailLevel: 'comprehensive'
}
})
return {
success: true,
analysis: response.data.analysis
}
} catch (error) {
return {
success: false,
error: error.response?.data?.error || 'ATS analysis failed'
}
}
}
// React component
function ATSAnalyzer({ resumeId }) {
const [analysis, setAnalysis] = useState(null)
const [jobDescription, setJobDescription] = useState('')
const [analyzing, setAnalyzing] = useState(false)
const runAnalysis = async () => {
if (!jobDescription.trim()) {
toast.error('Please enter job description')
return
}
setAnalyzing(true)
try {
// First get resume content
const resumeResponse = await apiClient.get(`/resumes/${resumeId}`)
const resumeContent = resumeResponse.data.resume.content
// Perform ATS analysis
const result = await analyzeATSCompatibility(resumeContent, jobDescription)
if (result.success) {
setAnalysis(result.analysis)
} else {
toast.error(result.error)
}
} finally {
setAnalyzing(false)
}
}
return (
<div className="ats-analyzer">
<textarea
value={jobDescription}
onChange={(e) => setJobDescription(e.target.value)}
placeholder="Paste job description..."
className="h-32 w-full rounded border p-3"
/>
<button
onClick={runAnalysis}
disabled={analyzing}
className="mt-4 rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
>
{analyzing ? 'Analyzing...' : 'Start ATS Analysis'}
</button>
{analysis && (
<div className="mt-6 space-y-4">
<div className="rounded bg-green-50 p-4">
<h3 className="font-semibold text-green-800">ATS Compatibility Score: {analysis.score}/100</h3>
</div>
<div className="rounded bg-blue-50 p-4">
<h4 className="font-semibold text-blue-800">Keyword Matches</h4>
<div className="mt-2 flex flex-wrap gap-2">
{analysis.keywords.matched.map((keyword, index) => (
<span key={index} className="rounded bg-green-200 px-2 py-1 text-sm text-green-800">
{keyword}
</span>
))}
</div>
</div>
<div className="rounded bg-yellow-50 p-4">
<h4 className="font-semibold text-yellow-800">Optimization Suggestions</h4>
<ul className="mt-2 space-y-1">
{analysis.suggestions.map((suggestion, index) => (
<li key={index} className="text-yellow-700">
{suggestion}
</li>
))}
</ul>
</div>
</div>
)}
</div>
)
}📊 Complete Application Example
Resume Management Dashboard
// components/ResumeDashboard.jsx
import { useState, useEffect } from 'react'
import { useSession } from 'next-auth/react'
function ResumeDashboard() {
const { data: session } = useSession()
const [resumes, setResumes] = useState([])
const [loading, setLoading] = useState(true)
const [stats, setStats] = useState({
totalResumes: 0,
totalViews: 0,
totalDownloads: 0
})
useEffect(() => {
if (session) {
fetchDashboardData()
}
}, [session])
const fetchDashboardData = async () => {
try {
const [resumesRes, statsRes] = await Promise.all([apiClient.get('/resumes'), apiClient.get('/dashboard')])
setResumes(resumesRes.data.resumes)
setStats(statsRes.data.stats)
} catch (error) {
console.error('Failed to fetch dashboard data:', error)
} finally {
setLoading(false)
}
}
const deleteResume = async (resumeId) => {
if (!confirm('Are you sure you want to delete this resume?')) return
try {
await apiClient.delete(`/resumes/${resumeId}`)
setResumes((prev) => prev.filter((r) => r.id !== resumeId))
toast.success('Resume deleted successfully')
} catch (error) {
toast.error('Delete failed')
}
}
const duplicateResume = async (resumeId) => {
try {
const response = await apiClient.post(`/resumes/${resumeId}/duplicate`)
const newResume = response.data.resume
setResumes((prev) => [newResume, ...prev])
toast.success('Resume duplicated successfully')
} catch (error) {
toast.error('Duplicate failed')
}
}
if (loading) {
return <div className="flex justify-center p-8">Loading...</div>
}
return (
<div className="dashboard">
{/* Stats cards */}
<div className="mb-8 grid grid-cols-1 gap-6 md:grid-cols-3">
<StatCard title="Total Resumes" value={stats.totalResumes} icon="📄" />
<StatCard title="Total Views" value={stats.totalViews} icon="👁️" />
<StatCard title="Total Downloads" value={stats.totalDownloads} icon="⬇️" />
</div>
{/* Resume list */}
<div className="rounded-lg bg-white shadow">
<div className="border-b p-6">
<h2 className="text-xl font-semibold">My Resumes</h2>
</div>
<div className="divide-y">
{resumes.map((resume) => (
<ResumeItem key={resume.id} resume={resume} onDelete={deleteResume} onDuplicate={duplicateResume} />
))}
</div>
</div>
</div>
)
}
function StatCard({ title, value, icon }) {
return (
<div className="rounded-lg bg-white p-6 shadow">
<div className="flex items-center">
<div className="mr-3 text-2xl">{icon}</div>
<div>
<p className="text-sm text-gray-600">{title}</p>
<p className="text-2xl font-bold">{value}</p>
</div>
</div>
</div>
)
}
function ResumeItem({ resume, onDelete, onDuplicate }) {
const { exportPDF, exporting } = useResumeExport()
return (
<div className="flex items-center justify-between p-6">
<div className="flex-1">
<h3 className="font-semibold">{resume.title}</h3>
<p className="text-sm text-gray-600">Updated on {new Date(resume.updatedAt).toLocaleDateString()}</p>
</div>
<div className="flex space-x-2">
<button
onClick={() => exportPDF(resume.id)}
disabled={exporting}
className="rounded bg-blue-600 px-3 py-1 text-sm text-white hover:bg-blue-700"
>
{exporting ? 'Exporting...' : 'Export PDF'}
</button>
<button
onClick={() => onDuplicate(resume.id)}
className="rounded bg-gray-600 px-3 py-1 text-sm text-white hover:bg-gray-700"
>
Duplicate
</button>
<button
onClick={() => onDelete(resume.id)}
className="rounded bg-red-600 px-3 py-1 text-sm text-white hover:bg-red-700"
>
Delete
</button>
</div>
</div>
)
}
export default ResumeDashboard🔗 Related Documentation
- API Overview - API basics and authentication
- Authentication System - Detailed authentication documentation
- Resume Management - Resume-related APIs
- User Management - User account APIs
- AI Services - AI feature APIs
- Webhooks - Webhook integration
📝 Notes
Best Practices
- Error Handling: Always handle API errors and provide user-friendly feedback
- Loading States: Provide appropriate loading indicators for asynchronous operations
- Data Validation: Validate data on both client and server sides
- Caching Strategy: Use caching reasonably to reduce unnecessary API calls
- Security: Never store sensitive information on the client side
Performance Optimization
- Use React.memo and useMemo to optimize component rendering
- Implement virtual scrolling for large datasets
- Use SWR or React Query for data management
- Combine multiple API calls to reduce network requests
Debugging Tips
- Use browser developer tools to monitor network requests
- Set up appropriate logging
- Use React DevTools to inspect component state
- Implement error boundaries to handle unexpected errors
Last updated: 12/30/2024 •Suggest improvements