Using AWS Amplify API in React
This guide shows you how to integrate the Legalesign GraphQL API into your React application using AWS Amplify. AWS Amplify provides a simple, type-safe way to make GraphQL queries and mutations from your React components.
What You'll Learn
- How to install and configure AWS Amplify in a React project
- How to authenticate with Legalesign's GraphQL API
- How to make queries and mutations using React hooks
- Best practices for error handling and loading states
- How to structure your API calls in a React application
Why Use AWS Amplify?
AWS Amplify provides several benefits for React developers:
- Simple API - Clean, promise-based interface for GraphQL
- Type Safety - Works well with TypeScript
- Authentication - Built-in support for AWS Cognito
- React Hooks - Easy integration with React components
- Automatic Retries - Handles network failures gracefully
Installation
Step 1: Install Dependencies
npm install aws-amplify
Step 2: Configure Amplify
Create a configuration file for your Amplify setup. This tells Amplify how to connect to the Legalesign API.
const amplifyConfig = {
API: {
GraphQL: {
endpoint: 'https://graphql.uk.legalesign.com/graphql',
region: 'eu-west-2',
defaultAuthMode: 'userPool'
}
},
Auth: {
Cognito: {
userPoolId: 'eu-west-2_NUPAjABy7',
userPoolClientId: '38kn0eb9mf2409t6mci98eqdvt',
region: 'eu-west-2'
}
}
};
export default amplifyConfig;
These are the production Legalesign API credentials. For development or testing, your account manager may provide different endpoints.
Step 3: Initialize Amplify
In your main application file, configure Amplify before rendering your app.
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Amplify } from 'aws-amplify';
import amplifyConfig from './amplifyconfiguration';
import App from './App';
// Configure Amplify
Amplify.configure(amplifyConfig);
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Authentication
Before making API calls, you need to authenticate with AWS Cognito.
Login Component Example
import { useState } from 'react';
import { signIn } from 'aws-amplify/auth';
const Login = ({ onLoginSuccess }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const handleLogin = async (e) => {
e.preventDefault();
setLoading(true);
setError(null);
try {
const { isSignedIn } = await signIn({
username: email,
password: password
});
if (isSignedIn) {
onLoginSuccess();
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleLogin}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Logging in...' : 'Login'}
</button>
{error && <p className="error">{error}</p>}
</form>
);
};
export default Login;
Getting the Access Token
Once authenticated, you can get the access token for API calls:
import { fetchAuthSession } from 'aws-amplify/auth';
const getAccessToken = async () => {
const session = await fetchAuthSession();
return session.tokens?.accessToken?.toString();
};
Making GraphQL Queries
Basic Query Example
Here's how to fetch user information:
import { useState, useEffect } from 'react';
import { generateClient } from 'aws-amplify/api';
const UserProfile = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
const client = generateClient();
try {
const result = await client.graphql({
query: `
query GetUser {
user {
id
firstName
lastName
email
}
}
`
});
setUser(result.data.user);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return <div>No user found</div>;
return (
<div>
<h2>{user.firstName} {user.lastName}</h2>
<p>{user.email}</p>
</div>
);
};
export default UserProfile;
Query with Variables
Here's how to fetch a specific template by ID:
import { useState, useEffect } from 'react';
import { generateClient } from 'aws-amplify/api';
const TemplateViewer = ({ templateId }) => {
const [template, setTemplate] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchTemplate = async () => {
const client = generateClient();
try {
const result = await client.graphql({
query: `
query GetTemplate($id: ID!) {
template(id: $id) {
id
title
pageCount
created
roles {
id
name
roleType
}
}
}
`,
variables: {
id: templateId
}
});
setTemplate(result.data.template);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
if (templateId) {
fetchTemplate();
}
}, [templateId]);
if (loading) return <div>Loading template...</div>;
if (error) return <div>Error: {error}</div>;
if (!template) return <div>Template not found</div>;
return (
<div>
<h2>{template.title}</h2>
<p>Pages: {template.pageCount}</p>
<p>Created: {new Date(template.created).toLocaleDateString()}</p>
<h3>Roles:</h3>
<ul>
{template.roles.map(role => (
<li key={role.id}>{role.name} ({role.roleType})</li>
))}
</ul>
</div>
);
};
export default TemplateViewer;
Making GraphQL Mutations
Creating a Template
import { useState } from 'react';
import { generateClient } from 'aws-amplify/api';
const CreateTemplate = ({ groupId, onTemplateCreated }) => {
const [title, setTitle] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
setError(null);
const client = generateClient();
try {
const result = await client.graphql({
query: `
mutation CreateTemplate($input: templateCreateInput!) {
createTemplate(input: $input) {
template {
id
title
groupId
created
}
}
}
`,
variables: {
input: {
groupId: groupId,
title: title
}
}
});
const newTemplate = result.data.createTemplate.template;
onTemplateCreated(newTemplate);
setTitle(''); // Reset form
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<h3>Create New Template</h3>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Template title"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Creating...' : 'Create Template'}
</button>
{error && <p className="error">{error}</p>}
</form>
);
};
export default CreateTemplate;
Custom Hook Pattern
For cleaner code, create custom hooks for your API calls:
import { useState, useEffect } from 'react';
import { generateClient } from 'aws-amplify/api';
export const useTemplates = (groupId) => {
const [templates, setTemplates] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchTemplates = async () => {
const client = generateClient();
try {
const result = await client.graphql({
query: `
query GetGroup($id: ID!) {
group(id: $id) {
templateConnection {
templates {
id
title
pageCount
created
}
}
}
}
`,
variables: { id: groupId }
});
setTemplates(result.data.group.templateConnection.templates);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
if (groupId) {
fetchTemplates();
}
}, [groupId]);
return { templates, loading, error };
};
Using the Custom Hook
import { useTemplates } from '../hooks/useTemplates';
const TemplateList = ({ groupId }) => {
const { templates, loading, error } = useTemplates(groupId);
if (loading) return <div>Loading templates...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h2>Templates</h2>
{templates.length === 0 ? (
<p>No templates found</p>
) : (
<ul>
{templates.map(template => (
<li key={template.id}>
<strong>{template.title}</strong>
<span> - {template.pageCount} pages</span>
</li>
))}
</ul>
)}
</div>
);
};
export default TemplateList;
File Upload in React
Here's a complete example of uploading a PDF template from a React component:
import { useState } from 'react';
import { generateClient } from 'aws-amplify/api';
const UploadTemplate = ({ groupId }) => {
const [title, setTitle] = useState('');
const [file, setFile] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(null);
const handleFileChange = (e) => {
const selectedFile = e.target.files[0];
if (selectedFile && selectedFile.type === 'application/pdf') {
setFile(selectedFile);
setError(null);
} else {
setError('Please select a PDF file');
setFile(null);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!file) {
setError('Please select a file');
return;
}
setLoading(true);
setError(null);
setSuccess(null);
const client = generateClient();
try {
// Step 1: Create template
const createResult = await client.graphql({
query: `
mutation CreateTemplate($input: templateCreateInput!) {
createTemplate(input: $input) {
template {
id
title
}
}
}
`,
variables: {
input: {
groupId: groupId,
title: title
}
}
});
const templateId = createResult.data.createTemplate.template.id;
// Step 2: Get upload URL
const uploadResult = await client.graphql({
query: `
query GetUploadUrl($id: ID!) {
upload(
id: $id,
uploadType: TEMPLATE,
extension: "pdf"
) {
url
}
}
`,
variables: { id: templateId }
});
const uploadUrl = uploadResult.data.upload.url;
// Step 3: Upload file
const response = await fetch(uploadUrl, {
method: 'PUT',
body: file,
headers: {
'Content-Type': 'application/pdf'
}
});
if (!response.ok) {
throw new Error('Upload failed');
}
setSuccess(`Template "${title}" created successfully!`);
setTitle('');
setFile(null);
// Reset file input
e.target.reset();
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<h3>Upload PDF Template</h3>
<div>
<label>Template Title:</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Enter template title"
required
/>
</div>
<div>
<label>PDF File:</label>
<input
type="file"
accept=".pdf"
onChange={handleFileChange}
required
/>
</div>
<button type="submit" disabled={loading || !file}>
{loading ? 'Uploading...' : 'Upload Template'}
</button>
{error && <p className="error">{error}</p>}
{success && <p className="success">{success}</p>}
</form>
);
};
export default UploadTemplate;
Best Practices
- Centralize API Configuration - Keep all Amplify config in one place
- Use Custom Hooks - Encapsulate API logic in reusable hooks
- Handle Errors Gracefully - Always provide user-friendly error messages
- Show Loading States - Keep users informed during API calls
- Validate Input - Check data before sending to API
- Secure Tokens - Never expose tokens in client-side code