Skip to main content

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.

src/amplifyconfiguration.js
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;
note

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.

src/main.jsx
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

src/components/Login.jsx
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:

src/components/UserProfile.jsx
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:

src/components/TemplateViewer.jsx
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

src/components/CreateTemplate.jsx
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:

src/hooks/useTemplates.js
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

src/components/TemplateList.jsx
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:

src/components/UploadTemplate.jsx
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

  1. Centralize API Configuration - Keep all Amplify config in one place
  2. Use Custom Hooks - Encapsulate API logic in reusable hooks
  3. Handle Errors Gracefully - Always provide user-friendly error messages
  4. Show Loading States - Keep users informed during API calls
  5. Validate Input - Check data before sending to API
  6. Secure Tokens - Never expose tokens in client-side code