API Calls
Learn how to make API calls in Chameleon
API Calls
Learn how to create API routes and make API calls in Chameleon using Next.js App Router.
Overview
Chameleon uses Next.js App Router for API routes and provides both server-side and client-side API calling capabilities. API routes are created in the src/app/api/ directory and can be called from components or other API routes.
API Routes
Basic API Route
// src/app/api/hello/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
return NextResponse.json({ message: "Hello, World!" });
}
export async function POST(request: NextRequest) {
const body = await request.json();
return NextResponse.json({ received: body });
}
API Route with Authentication
// src/app/api/user/profile/route.ts
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/auth";
export async function GET(request: NextRequest) {
const session = await auth();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.json({
user: {
id: session.user.uuid,
email: session.user.email,
name: session.user.nickname,
},
});
}
API Route with Database
// src/app/api/posts/route.ts
import { NextRequest, NextResponse } from "next/server";
import { getPosts, createPost } from "@/models/post";
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get("page") || "1");
const limit = parseInt(searchParams.get("limit") || "10");
const posts = await getPosts({ page, limit });
return NextResponse.json({
posts,
pagination: {
page,
limit,
total: posts.length,
},
});
} catch (error) {
return NextResponse.json(
{ error: "Failed to fetch posts" },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const body = await request.json();
const post = await createPost({
...body,
author_id: session.user.uuid,
});
return NextResponse.json(post, { status: 201 });
} catch (error) {
return NextResponse.json(
{ error: "Failed to create post" },
{ status: 500 }
);
}
}
Client-Side API Calls
Using Fetch
// src/components/api-example.tsx
"use client";
import { useState, useEffect } from "react";
export function ApiExample() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch("/api/hello");
if (!response.ok) {
throw new Error("Failed to fetch data");
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return <div>{JSON.stringify(data)}</div>;
}
API Call with Authentication
// src/components/user-profile.tsx
"use client";
import { useState } from "react";
import { useSession } from "next-auth/react";
export function UserProfile() {
const { data: session } = useSession();
const [profile, setProfile] = useState(null);
const fetchProfile = async () => {
try {
const response = await fetch("/api/user/profile", {
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error("Failed to fetch profile");
}
const data = await response.json();
setProfile(data.user);
} catch (error) {
console.error("Error fetching profile:", error);
}
};
if (!session) {
return <div>Please sign in to view your profile.</div>;
}
return (
<div>
<button onClick={fetchProfile}>Load Profile</button>
{profile && (
<div>
<h2>{profile.name}</h2>
<p>{profile.email}</p>
</div>
)}
</div>
);
}
Form Submission with API
// src/components/contact-form.tsx
"use client";
import { useState } from "react";
export function ContactForm() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [message, setMessage] = useState("");
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsSubmitting(true);
setMessage("");
try {
const formData = new FormData(e.currentTarget);
const data = {
name: formData.get("name"),
email: formData.get("email"),
message: formData.get("message"),
};
const response = await fetch("/api/contact", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error("Failed to send message");
}
setMessage("Message sent successfully!");
e.currentTarget.reset();
} catch (error) {
setMessage("Error sending message. Please try again.");
} finally {
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
name="name"
type="text"
placeholder="Your Name"
required
/>
</div>
<div>
<input
name="email"
type="email"
placeholder="Your Email"
required
/>
</div>
<div>
<textarea
name="message"
placeholder="Your Message"
required
/>
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Sending..." : "Send Message"}
</button>
{message && <div>{message}</div>}
</form>
);
}
Error Handling
API Route Error Handling
// src/app/api/example/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function GET(request: NextRequest) {
try {
// Your API logic here
const result = await someAsyncOperation();
return NextResponse.json({ success: true, data: result });
} catch (error) {
console.error("API Error:", error);
return NextResponse.json(
{
success: false,
error: "Internal server error",
message: error instanceof Error ? error.message : "Unknown error"
},
{ status: 500 }
);
}
}
Client-Side Error Handling
// src/lib/api-client.ts
export class ApiError extends Error {
constructor(
message: string,
public status: number,
public response?: Response
) {
super(message);
this.name = "ApiError";
}
}
export async function apiCall<T>(
url: string,
options: RequestInit = {}
): Promise<T> {
const response = await fetch(url, {
headers: {
"Content-Type": "application/json",
...options.headers,
},
...options,
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new ApiError(
errorData.message || "API request failed",
response.status,
response
);
}
return response.json();
}
// Usage
try {
const data = await apiCall<UserProfile>("/api/user/profile");
console.log(data);
} catch (error) {
if (error instanceof ApiError) {
console.error(`API Error ${error.status}: ${error.message}`);
} else {
console.error("Unexpected error:", error);
}
}
File Locations
src/app/api/- API route handlerssrc/lib/api-client.ts- API client utilitiessrc/services/- Business logic servicessrc/models/- Data models and database operations
Common Tasks
Create a New API Route
- Create directory:
src/app/api/your-endpoint/ - Add
route.tsfile - Export HTTP method functions (GET, POST, PUT, DELETE)
- Add proper error handling
- Test the endpoint
Add Authentication to API Route
import { auth } from "@/auth";
export async function GET() {
const session = await auth();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Your protected logic here
}
Handle Form Data
export async function POST(request: NextRequest) {
const formData = await request.formData();
const file = formData.get("file") as File;
const name = formData.get("name") as string;
// Process form data
}
Add CORS Headers
export async function GET() {
const response = NextResponse.json({ data: "example" });
response.headers.set("Access-Control-Allow-Origin", "*");
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.headers.set("Access-Control-Allow-Headers", "Content-Type");
return response;
}
Troubleshooting
API route not working
Problem: API endpoint returns 404
Solution:
- Check file is named
route.ts - Verify directory structure is correct
- Check for TypeScript errors
- Restart development server
CORS errors
Problem: Client can't call API from different domain
Solution:
- Add CORS headers to API response
- Configure middleware for CORS
- Use same domain for API calls
- Check browser network tab for errors
Authentication not working
Problem: API returns 401 even with valid session
Solution:
- Check session is properly retrieved
- Verify authentication middleware
- Check session cookie settings
- Test with different authentication methods
Next Steps
- New Component - Create components that use APIs
- Authentication - Secure API endpoints
- Database - Connect APIs to database