Module 4: Add a GraphQL API and Database
In this module, you will use the Amplify CLI and libraries to configure and add a GraphQL API to your app
Introduction
Implementation
Create a GraphQL API service and a database
1. Add the Amplify API
Open the Terminal, navigate to your project root directory , and run the following command:
amplify add api
2. Configure the API
When prompted, make the following selections:
Select from one of the below mentioned services:
❯ GraphQL
Authorization modes:
Choose the default authorization type for the API
❯ Amazon Cognito User Pool
Configure additional auth types?
N
3. Confirm selections
Validate the selected options, and choose Continue.
Here is the GraphQL API that we will create. Select a setting to edit or continue (Use arrow keys)
Name: gettingstarted
Authorization modes: Amazon Cognito User Pool (default)
Conflict detection (required for DataStore): Disabled
❯ Continue
4. Edit the schema
Select Blank Schema and choose Y when asked to edit the schema:
Choose a schema template:
❯ Blank Schema
Do you want to edit the schema now?
Y
5. Update the schema
As we want to represent the model we previously defined in the Note.swift file, use the following schema and save the file:
type Note
@model
@auth (rules: [ { allow: owner } ]) {
id: ID!
name: String!
description: String
image: String
}
The data model is made of one class named Note and four String properties: id and name are mandatory; description and image are optional.
The @model transformer indicates we want to create a database to store these data.
The @auth transformer adds authentication rules to allow access to these data. For this project, we want only the owner of Notes to have access to them.
Delete the Note.swift file, we will re-generate the models in the next step.
Generate client-side code
Amplify generates client-side code.
1. Generate the code
To generate the code, run the following command in your terminal:
amplify codegen models
This creates Swift files in the amplify/generated/models directory and automatically add them to your project.
Deploy the API service and database
1. Deploy the backend database
To deploy the backend API and database we have just created, in your terminal run the following command:
amplify push
2. Push the deployment
When prompted, make the following selections:
amplify push
Are you sure you want to continue?
Y
Do you want to generate code for your newly created GraphQL API?
N
Add API client library to the project
1. Open the general tab
Navigate to the General tab of your Target application (Your Project > Targets > General), and select the plus (+) in the Frameworks, Libraries, and Embedded Content section.

2. Choose the plugin
Choose the AWSAPIPlugin , and select Add.

3. Verify the dependency created
You have now added AWSAPIPlugin as a dependency for your project.

Configure the Amplify API library at runtime
1. Configure the library
Navigate back to Xcode , and open the GettingStartedApp.swift file.
To configure Amplify API, you will need to:
Add the import AWSAPIPlugin statement.
Create the AWSAPIPlugin plugin and register it with Amplify.
Your code should look like the following:
GettingStartedApp.swift file code
Add this code to your file
import Amplify
import AWSAPIPlugin
import AWSCognitoAuthPlugin
import SwiftUI
@main
struct GettingStartedApp: App {
init() {
do {
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.add(plugin: AWSAPIPlugin(modelRegistration: AmplifyModels()))
try Amplify.configure()
print("Initialized Amplify");
} catch {
print("Could not initialize Amplify: \(error)")
}
}
var body: some Scene {
WindowGroup {
LandingView()
.environmentObject(AuthenticationService())
}
}
}
Create a class to support API CRUD operations
1. Create a NotesService.swift file
Create a new Swift file named NotesService.swift with the following code.
This class allows to fetch all notes, save a new note, and delete an existing note, while also publishing the fetched notes in a notes array.
NotesService.swift file code
Add this code to your file
import Amplify
import SwiftUI
@MainActor
class NotesService: ObservableObject {
@Published var notes: [Note] = []
func fetchNotes() async {
do {
let result = try await Amplify.API.query(request: .list(Note.self))
switch result {
case .success(let notesList):
print("Fetched \(notesList.count) notes")
notes = notesList.elements
case .failure(let error):
print("Fetch Notes failed with error: \(error)")
}
} catch {
print("Fetch Notes failed with error: \(error)")
}
}
func save(_ note: Note) async {
do {
let result = try await Amplify.API.mutate(request: .create(note))
switch result {
case .success(let note):
print("Save note completed")
notes.append(note)
case .failure(let error):
print("Save Note failed with error: \(error)")
}
} catch {
print("Save Note failed with error: \(error)")
}
}
func delete(_ note: Note) async {
do {
let result = try await Amplify.API.mutate(request: .delete(note))
switch result {
case .success(let note):
print("Delete note completed")
notes.removeAll(where: { $0.id == note.id })
case .failure(let error):
print("Delete Note failed with error: \(error)")
}
} catch {
print("Delete Note failed with error: \(error)")
}
}
}
Update the existing UI
1. List notes
Make the following changes to the NotesView.swift file:
Add a new @EnvironmentObject private var notesService: NotesService property
Delete the local notes array and instead use published notesService.notes when creating the List items in the ForEach loop.
Call notesService.fetchNotes() when the view appears. We can do this using the task(priority:_:) method.
Your file should look like the following code.
NotesView.swift file code
Modify your code with this code
struct NotesView: View {
@EnvironmentObject private var authenticationService: AuthenticationService
@EnvironmentObject private var notesService: NotesService
var body: some View {
NavigationStack{
List {
if notesService.notes.isEmpty {
Text("No notes")
}
ForEach(notesService.notes, id: \.id) { note in
NoteView(note: note)
}
}
.navigationTitle("Notes")
.toolbar {
Button("Sign Out") {
Task {
await authenticationService.signOut()
}
}
}
}
.task {
await notesService.fetchNotes()
}
}
}
2. Set the notesService object
Since the notesService variable is marked with a @EnvironmentObject property wrapper annotation, we need to set it using the environmentObject(_:) view modifier on an ancestor view.
Update the GettingStartedApp.swift body to set the NotesService object:
notesService.swift file code
Modify your code with this code
var body: some Scene {
WindowGroup {
LandingView()
.environmentObject(NotesService())
.environmentObject(AuthenticationService())
}
}
3. Create notes
Create a new Swift file named SaveNoteView.swift with the following content.
This view displays text fields for a name, description, and image. It also has a Save Note button that when tapped, it creates a Note object and calls noteService.save(_:) to save it.
Note: The view automatically dismisses itself by the usage of the @Environment(\.dismiss) property.
SaveNoteView.swift file code
Add this code to your file
import SwiftUI
struct SaveNoteView: View {
@Environment(\.dismiss) private var dismiss
@EnvironmentObject private var notesService: NotesService
@State private var name = ""
@State private var description = ""
@State private var image = ""
var body: some View {
Form {
Section("Details") {
TextField("Name", text: $name)
TextField("Description", text: $description)
}
Section("Picture") {
TextField("Image Name", text: $image)
}
Button("Save Note") {
let note = Note(
name: name,
description: description.isEmpty ? nil : description,
image: image.isEmpty ? nil : image
)
Task {
await notesService.save(note)
dismiss()
}
}
}
}
}
4. Update the NotesView.swift file
Update the NotesView.swift file with the following information:
Add a new @State private var isSavingNote = false property
Add a new ToolbarItem at the bottom, with a "⨁ New Note" button that sets isSavingNote = true
Show SaveNoteView() inside a sheet(isPresented:) method, using the isSavingNote property.
Your file should now look like the following:
Updated SaveNoteView.swift file code
Update your code with this code
struct NotesView: View {
@EnvironmentObject private var authenticationService: AuthenticationService
@EnvironmentObject private var notesService: NotesService
@State private var isSavingNote = false
var body: some View {
NavigationStack{
List {
if notesService.notes.isEmpty {
Text("No notes")
}
ForEach(notesService.notes, id: \.id) { note in
NoteView(note: note)
}
}
.navigationTitle("Notes")
.toolbar {
Button("Sign Out") {
Task {
await authenticationService.signOut()
}
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("⨁ New Note") {
isSavingNote = true
}
.bold()
}
}
.sheet(isPresented: $isSavingNote) {
SaveNoteView()
}
}
.task {
await notesService.fetchNotes()
}
}
}
5. Delete notes
To implement this we can rely on the onDelete(perform:) method of ForEach:
Delete functionality code
Add this code to your code
ForEach(notesService.notes, id: \.id) { note in
NoteView(note: note)
}
.onDelete { indices in
for index in indices {
let note = notesService.notes[index]
Task {
await notesService.delete(note)
}
}
}
6. Verify your code is correct
Your NotesView.swift file should look like the following:
Updated NotesView.swift file code
Your code should look like this.
import SwiftUI
struct NotesView: View {
@EnvironmentObject private var authenticationService: AuthenticationService
@EnvironmentObject private var notesService: NotesService
@State private var isSavingNote = false
var body: some View {
NavigationStack{
List {
if notesService.notes.isEmpty {
Text("No notes")
}
ForEach(notesService.notes, id: \.id) { note in
NoteView(note: note)
}
.onDelete { indices in
for index in indices {
let note = notesService.notes[index]
Task {
await notesService.delete(note)
}
}
}
}
.navigationTitle("Notes")
.toolbar {
Button("Sign Out") {
Task {
await authenticationService.signOut()
}
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("⨁ New Note") {
isSavingNote = true
}
.bold()
}
}
.sheet(isPresented: $isSavingNote) {
SaveNoteView()
}
}
.task {
await notesService.fetchNotes()
}
}
}
Build and test
1. Run the project
To verify everything works as expected, build, and run the project.
Choose the ► button in the toolbar. Alternatively, you can also do it by navigating to Product -> Run , or by pressing Cmd + R .
The iOS simulator will open and the app should show you the Notes view, assuming you are still signed in.
2. Create a new note
Choose the " ⨁ New Note " button at the bottom to create a new list.

3. Enter details
Enter details for the note and choose Save Note.

4. View note
View the note in the list.

5. Delete the note
You can delete a note by swiping from the left of its row.

Conclusion
Add the ability to store images
Did you find what you were looking for today?
Let us know so we can improve the quality of the content on our pages