This project demonstrates how to integrate ShipEngine's shipping APIs with a Next.js application. It provides a complete implementation of shipping rate calculation, label generation, and shipment tracking functionality using ShipEngine's powerful API.
- π Real-time Shipping Rates: Get accurate shipping rates from multiple carriers
- π¦ Label Generation: Create shipping labels with a single click
- π Shipment Tracking: Track shipments in real-time
- π Dark/Light Mode: Fully responsive UI with theme support
- π± Mobile-Friendly: Responsive design that works on all devices
- Node.js 18.x or later
- npm or yarn
- ShipEngine API key (sign up at ShipEngine)
git clone https://github.com/yourusername/shipengine-with-nextjs.git
cd shipengine-with-nextjsnpm install
# or
yarn installCreate a .env.local file in the root directory with your ShipEngine API key:
SHIPENGINE_API_KEY=your_api_key_here
# Optional: Add specific carrier IDs if you have them
SHIPENGINE_FIRST_COURIER=carrier_id_1
SHIPENGINE_SECOND_COURIER=carrier_id_2
SHIPENGINE_THIRD_COURIER=carrier_id_3
SHIPENGINE_FOURTH_COURIER=carrier_id_4
SHIPENGINE_FIFTH_COURIER=carrier_id_5
npm run dev
# or
yarn devOpen http://localhost:3000 in your browser to see the application.
βββ app/ # Next.js app directory
β βββ api/ # API routes
β β βββ shipengine/ # ShipEngine API endpoints
β β βββ get-rates/ # Shipping rates endpoint
β β βββ label/ # Label creation endpoint
β β βββ tracking/ # Tracking endpoint
β βββ components/ # React components
β β βββ Footer.tsx # Footer component
β β βββ Navbar.tsx # Navigation component
β βββ tracking/ # Tracking page
β βββ globals.css # Global styles
β βββ layout.tsx # Root layout
β βββ page.tsx # Home page
βββ lib/ # Utility functions
β βββ data.ts # Sample data
β βββ shipEngine.ts # ShipEngine client setup
β βββ types.ts # TypeScript types
βββ public/ # Static assets
βββ .env.local # Environment variables (create this)
βββ next.config.js # Next.js configuration
βββ package.json # Project dependencies
βββ README.md # Project documentation
βββ tsconfig.json # TypeScript configuration
First, create a ShipEngine client instance using your API key:
// lib/shipEngine.ts import { ShipEngine } from 'shipengine'; export const shipengine = new ShipEngine(process.env.SHIPENGINE_API_KEY!);
// app/api/shipengine/get-rates/route.ts import { shipengine } from '@/lib/shipEngine'; import { NextResponse } from 'next/server'; export async function POST(request: Request) { try { const { toAddress } = await request.json(); // Define shipment details const shipmentDetails = { rateOptions: { carrierIds: [ // Your carrier IDs ], }, shipment: { validateAddress: 'validate_and_clean', shipTo: toAddress, shipFrom: { // Your warehouse address }, packages: [ // Package details ], }, }; // Get rates from ShipEngine const ratesResult = await shipengine.getRatesWithShipmentDetails(shipmentDetails); return NextResponse.json(ratesResult); } catch (error) { console.error('Error getting rates:', error); return NextResponse.json({ error: 'Failed to get shipping rates' }, { status: 500 }); } }
// app/api/shipengine/label/route.ts import { shipengine } from '@/lib/shipEngine'; import { NextResponse } from 'next/server'; export async function POST(request: Request) { try { const { rateId } = await request.json(); if (!rateId) { return NextResponse.json({ error: 'Rate ID is required' }, { status: 400 }); } // Create label using the selected rate const label = await shipengine.createLabelFromRate({ rateId, validateAddress: 'validate_and_clean', }); return NextResponse.json(label); } catch (error) { console.error('Error creating label:', error); return NextResponse.json({ error: 'Failed to create shipping label' }, { status: 500 }); } }
// app/api/shipengine/tracking/[labelId]/route.ts import { shipengine } from '@/lib/shipEngine'; import { NextResponse } from 'next/server'; export async function GET( request: Request, { params }: { params: { labelId: string } } ) { try { const labelId = params.labelId; if (!labelId) { return NextResponse.json({ error: 'Label ID is required' }, { status: 400 }); } // Track shipment using label ID const tracking = await shipengine.trackUsingLabelId({ labelId, }); return NextResponse.json(tracking); } catch (error) { console.error('Error tracking shipment:', error); return NextResponse.json({ error: 'Failed to track shipment' }, { status: 500 }); } }
const fetchRates = async () => { try { setLoading(true); setError(''); const response = await fetch('/api/shipengine/get-rates', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ toAddress: shippingAddress, }), }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Failed to get shipping rates'); } setRates(data.rates); } catch (error) { setError(error.message); } finally { setLoading(false); } };
const createLabel = async () => { try { setLabelLoading(true); setError(''); const response = await fetch('/api/shipengine/label', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ rateId, }), }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Failed to create shipping label'); } setLabel(data); } catch (error) { setError(error.message); } finally { setLabelLoading(false); } };
const trackShipment = async (labelId) => { try { setTrackingLoading(true); setTrackingError(''); const response = await fetch(`/api/shipengine/tracking/${labelId}`); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Failed to track shipment'); } setTrackingData(data); } catch (error) { setTrackingError(error.message); } finally { setTrackingLoading(false); } };
- Error Handling: Always implement proper error handling for API calls
- Loading States: Show loading indicators during API requests
- Validation: Validate user input before sending to the API
- Environment Variables: Keep API keys secure in environment variables
- TypeScript: Use TypeScript for better type safety and developer experience
Built with β€οΈ by Daniel Hashmi using Next.js and ShipEngine