https://www.courier.com/blog/introducing-puppygram-powered-by-courier-inbox-next-js-and-inngest/ Product Docs Resources Customers Integrations Pricing Log InSign Up Most Popular The Three Things to Never Build In Your App: Authentication, Notifications, and Payments --------------------------------------------------------------------- Three Ways to Send Emails Using Python (With Code Examples) --------------------------------------------------------------------- How to Send Emails with Attachments Using Amazon SES and S3 --------------------------------------------------------------------- Announcing Courier's $35M Series B Create Your Free Developer Account Courier makes it easy to send SMS, email, push and in-app notifications with a single API call. We have a REST API, client SDKs and Docs that you'll love Sign-up All Posts puppygram-header TUTORIAL Introducing Puppygram, powered by Courier Inbox, Next.js and Inngest CR Carter Rabasa June 13, 2023 Today we launched Courier Inbox, a set of APIs and UI components for building a modern application inbox. Inbox components are available for both web, iOS and Android applications and are totally customizable so that devs can build a notification inbox that feels like a native part of the rest of the application. To test this proposition, we decided to use Courier Inbox to build something lightyears away from a product inbox. I thought long and hard about what to build and, perhaps sensing my creative block, my dog Otto came up and asked for this afternoon walk. otto-puppygram And just like that, Puppygram was born! Puppgram is an Instagram clone that is built for iOS and is powered by Courier Inbox, Next.js and Inngest. In this blog post we're going to cover: * Creating a fresh Next.js web application * Configuring Courier to send Inbox notifications * Fetching random pictures of cute dogs using the random.dog API * Using Inngest to send a new dog notification to our Inbox every minute * Creating the Puppygram iOS experience using the Courier iOS SDK There are a few prerequisites for completing this tutorial: * Node.js * Xcode for Mac * Courier and Inngest accounts When we're done, we'll have an app that looks like this: Puppygram screen shot You can find the full source code for the Puppygram Server and Puppygram iOS app on Github and a live demo of this app hosted on Vercel. Creating a Next.js web application In order to build a Next.js app, you'll need to have Node.js installed. My preference these days is to use NVM (Node Version Manager) to install Node.js. It makes it easy to install multiple versions of Node.js and switch between them in your projects. Once you've installed Node.js, open up a terminal and run the following command to install Next.js: 1 npx create-next-app@latest You'll be prompted to answer several questions, but it's fine to stick to the defaults. Once this process is complete, a new directory will be created and loaded with all of the default files for this app. Change into this new directory and create a .env.local file to store secrets for Courier and Inngest. We'll populate this file while we're building and testing on localhost, and you'll just need to remember to copy these environment variables to whatever platform or infra you deploy your app to. Get Courier API Credentials Log-in to your Courier account and click on the gear icon and then API Keys. When you create a Courier account, we automatically create two Workspaces for you, one for testing and one for production. Each workspace has its own set of data and API keys. For simplicity, we're going to stick to the "production" workspace. Copy the "published" production API Key and paste into into .env.local using the following key: 1 COURIER_AUTH_TOKEN=pk_XXX Configure Your Courier Inbox Provider Click on "Channels" in the left nav. Channels represent the different mediums that a user can receive a notification on. Courier supports all the most popular channels, including SMS, email, mobile push and others. For each channel, you'll see a list of Providers. For instance, Courier supports multiple SMS providers, including Twilio, MessageBird and Vonage just to name a few. In our case, scroll down and select the "Courier Inbox" provider. When the page opens, scroll to the bottom and click "install". You are now ready to send notifications to the "inbox" channel using the "Courier Inbox" provider. Creating a Public Inbox Courier Inbox is primarily designed to display notifications to individual users, but in our case the Inbox and its content will be viewed by anyone using the app. Still, we need to create a "user" to send these notifications to, so click on "Users" in the left navigation and create a new user. The only required field is user_id, so just go ahead and enter puppygram and click "Save". In your .env.local, create the following variable: 1 NEXT_PUBLIC_COURIER_USER=puppygram Get Inngest Credentials Inngest is a workflow-as-code service that makes it easy to build reliable serverless workflows in your current codebase, without any new infrastructure. We're going to use Inngest to create a cron job that sends puppy pics to our app once every minute. Log-in to your Inngest account and click on "Manage" in the top nav. Click on "Event Keys" in the subnav. Event Keys are used to bundle similar events. This helps when navigating the Inngest dashboard, debugging, etc. The recommendation is that you create a new Event Key for every combination of environment and application. In our case, we're just going to use the "Default ingest key", but you can easily create new Event Keys to use. Copy the value for this key and paste it into your app's .env.local file: 1 INNGEST_EVENT_KEY=xxx Next, click on "Signing Key". The Signing Key is used by the Inngest SDK to securely communicate with your application. Copy the value for this key and paste it into your app's .env.local file: 1 INNGEST_SIGNING_KEY=signkey-prod-yyy Let's Start Coding! Ok, now that we have our services and configuration out of the way, let's dive into the code. We're going to start by building the server-side web application that is responsible for sending pictures of puppies to our iOS app. Our web application will be designed to: * Wake up once a minute * Select a random picture of a dog * Send an inbox notification with the URL of the picture of the random dog Using Inngest to Schedule Jobs With just a few lines of code, we are going to wire up our web application to Inngest so that the service can call into our application once a minute to trigger a new notification to our Courier Inbox. We're going to breeze through their Quickstart, which you can review in more detail later. In the root of your project, run the following command to install the Inngest SDK: 1 npm install inngest Create a new directory called inngest in your project root. Create a file called client.js in this new directory: 1 import { Inngest } from "inngest"; 2 3 // Create a client to send and receive events 4 export const inngest = new Inngest({ name: "Puppygram Next.js" }); Now create a route handler to handle the /api/inngest route. Create a directory in app called api and a directory in api called inngest. Create a file called route.js: 1 import { serve } from "inngest/next"; 2 import { inngest } from "../../../inngest/client"; 3 4 export const { GET, POST, PUT } = serve(inngest, []); Finally, let's create an Inngest function that prints "Hello Puppygram" every minute. Edit route.js and paste this function just below the imports: 1 export const sendNotification = inngest.createFunction( 2 { name: "Puppygram Send Notification" }, 3 { cron: "* * * * *" }, 4 async () => { 5 console.log("Hello Puppygram!") 6 } 7 ) Now, update the serve call at the bottom of the file to include this new Inngest function that has been created: 1 export const { GET, POST, PUT } = serve(inngest, [ 2 sendNotification 3 ]) In your terminal, go ahead and start your web application: 1 npm run dev Open up another terminal and run: 1 npx inngest-cli@latest dev This will execute a localhost version of the Inngest service. This will connect to your web application (running on port 3000) and begin executing your cron job. Go back to the terminal you launched your web app in, and every minute you should see this print out: 1 Hello Puppygram! Getting Pictures of Random Dogs Thanks to our friends at the Random Dog API, we have a service that we can use to get pictures of very cute, very random dogs. The API supports 3 different endpoints: * /woof - return the ID of a random dog * /woof.json - return a JSON payload of a random dog * /doggos - return JSON array of all dog IDs Since our service is going to wake up once a minute to send a notification with a picture of a random dog, we didn't want to burden this free service with all those API calls. So instead we invoked the /doggos endpoint and copied that information into a file in our project. Create a directory at the project root called data and a file in it called doggos.json. Paste the following into that file: 1 [ 2 "00186969-c51d-462b-948b-30a7e1735908.jpg", 3 "00b417af-0b5f-42d7-9ad0-6aab6c3db491.jpg", 4 "027eef85-ccc1-4a66-8967-5d74f34c8bb4.jpg", 5 "02f1d7d0-9ff7-44af-8066-dd9247ebe74d.jpg", 6 "03024628-188b-408e-a853-d97c9f04f903.jpg", 7 "0356c15a-8874-4af3-a02a-ed0ae8d62b55.jpg" 8 ] Now, let's update our sendNotification function in app/api/inngest/ routes.js to pick a random image and print it out to the console. Add the following import to the top of the file: 1 import doggos from '../../../data/doggos.json' assert { type: 'json' } Update the function to select a random image from the array and print it out: 1 export const sendNotification = inngest.createFunction( 2 { name: "Puppygram Send Notification" }, 3 { cron: "* * * * *" }, 4 async () => { 5 // get random dog photo 6 const randomIndex = Math.floor(Math.random() * doggos.length) 7 const image = doggos[randomIndex] 8 console.log(image) 9 } 10 ) Reset your Next.js dev server, and you should see random image filenames being printed out once a minute. Now it's time to send those to Courier Inbox! Sending Random Dog Pics to Courier Inbox The final part of the server-side component of Puppygram is the code to send the random dog pic to Courier Inbox. Luckily, this is exceedingly easy. First, let's add the Courier Node.js SDK to our project: 1 npm i @trycourier/courier Next, import the module and initialize the API client at the top of route.js: 1 import { CourierClient } from '@trycourier/courier' 2 3 const courier = CourierClient() Finally, in the body of our sendNotification function, make the API call to Courier: 1 export const sendNotification = inngest.createFunction( 2 { name: "Puppygram Send Notification" }, 3 { cron: "* * * * *" }, 4 async () => { 5 // get random dog photo 6 const randomIndex = Math.floor(Math.random() * doggos.length) 7 const image = doggos[randomIndex] 8 // send a notification with the URL to the random image 9 await courier.send({ 10 message: { 11 to: { 12 user_id: process.env.NEXT_PUBLIC_COURIER_USER 13 }, 14 content: { 15 title: "", 16 body: "Meet my fluffy and playful partner in crime, always ready for some mischief! #AdorableTroublemaker" 17 }, 18 data: { 19 image_url: `https://random.dog/${image}` 20 }, 21 routing: { 22 method: "single", 23 channels: [ 24 "inbox" 25 ] 26 } 27 } 28 }) 29 } 30 ) You're done! Now, all you need to do is deploy this web app (there are many options for deploying a Next.js application) and to deploy your serverless function to Inngest. Make sure your web application is fully deployed and live before you deploy the function to Inngest. Now, let's turn our attention to building the iOS app Creating an iOS App In order to build an iOS app, you'll need to install Xcode. I'll wait... < 2 hours later > Hey, welcome back! Ok, now that you've downloaded several gigabytes of IDE, let's start building an iOS app! Create a new app Create a new Project, select "iOS" and "App" and click "Next". Give it the name "Puppygram", make sure "SwiftUI" is selected and click "Next". Create a new folder for your project and then click "Create". Install the Courier iOS SDK Now that you've created your project, let's install the Courier iOS SDK. Select the "Puppygram" project in the top left of your application nav and click "Package Dependencies" on the right. Click the "+" sign at the bottom and you'll be prompted to add a dependency. Paste this link in: 1 https://github.com/trycourier/courier-ios Click "Add Package" to add this to your project. That's it! You now have the power of Courier at your fingertips. Building the View For the purpose of this blog post, we're going to build a single screen app that has a list view that updates in real-time to display the notifications we are sending the app. In your project, open up the ContentView file. At the top of the file, import the Courier iOS SDK: 1 import Courier_iOS Underneath the ContentView declaration, create a messages instance variable to hold the Inbox messages that we receive from Courier: 1 struct ContentView: View { 2 @State private var messages: [InboxMessage] = [] 3 var body: some View { Immediately following the .padding() call, add an onAppear event handler with the following code. 1 .padding() 2 .onAppear{ 3 Task { 4 try await 5 // Sign-in to Courier 6 Courier.shared.signIn(accessToken:"",clientKey:"",userId:"") 7 // Add an Inbox listener 8 Courier.shared.addInboxListener( 9 onInitialLoad: { }, 10 onError: { error in }, 11 onMessagesChanged: { messages, unreadMessageCount, totalMessageCount, canPaginate in 12 // update the messages array when new messages come in 13 self.messages = messages 14 } 15 ) 16 } 17 } This code signs the user into Courier and wires up an event handler to update the messages instance variable when new messages come in. In order to get the Courier.shared.signIn call working properly, you need to replace those 3 values. Let's go one by one. Authenticating the iOS SDK Replace with the to value you are using in your web app when you send a notification using Courier. In our case, the value is puppygram. Replace with the value of the Courier Public Key for your Courier Inbox Provider. This key is completely safe to use on the client, in both web and mobile apps. Finally, we need to set the accessToken property. In order to do this safely, it is recommended that developers generate JWTs for their users that specify scope and expiration information. For the purposes of this tutorial, we're going to use the Courier API to generate a JWT on the command line. Open up a terminal, and run the following command substituting your values for and : 1 curl --request POST \ 2 --url https://api.courier.com/auth/issue-token \ 3 --header 'Accept: application/json' \ 4 --header 'Authorization: Bearer ' \ 5 --header 'Content-Type: application/json' \ 6 --data ' 7 { 8 "scope": "user_id: read:messages", 9 "expires_in": "2 days" 10 } 11 ' You'll get back some JSON that looks like this: 1 {"token":"eyzzz"} Copy the value of of the token property and paste it into the authToken parameter for Courier.shared.signIn. You're are totally authenticated! Displaying the Messages Now that we're authenticated, the last step for our app is to display the messages. Delete the code below: 1 Image(systemName: "globe") 2 .imageScale(.large) 3 .foregroundColor(.accentColor) 4 Text("Hello, world!") Replace it with this: 1 List { 2 ForEach(messages, id: \.self) { message in 3 AsyncImage(url: URL(string: message.imageUrl)){ image in 4 image.resizable() 5 } placeholder: { 6 ProgressView() 7 } 8 .frame(width: 300, height: 300) 9 .clipShape(RoundedRectangle(cornerRadius: 25)) 10 VStack { 11 Text(message.subtitle ?? "") 12 } 13 } 14 } This is the simplest way to define a List and the elements inside of it. For now, we're just going to just display the image (passed-in via the custom data field of the REST API call) and the image description which we are passing in the body field of the REST API call. Important Note: In the iOS SDK, the subtitle property of the message object maps to the value of body in the REST API call. Now, if you try to build this it will fail. This is because we need to tell the iOS SDK that we're passing-in a custom field in the REST API call. You can do this by extending the InboxMessage class. Just paste this code at the top of your file, just below the imports: 1 extension InboxMessage { 2 var imageUrl: String { 3 get { 4 return (data?["image_url"] as? String) ?? "" 5 } 6 } 7 } If all goes well, you can press "play" and you should see something like this running in the simulator: puppygram-prototype Wrapping Things Up Ok, we covered a LOT of territory building an Instagram clone for cute dogs. We learned how to: * Create a fresh Next.js web application * Configure Courier to send Inbox (in-app) notifications * Fetch random pictures of cute dogs using the random.dog API * Use Inngest to send a new dog notification to our Inbox every minute * Create a fresh iOS app * Use the Courier iOS SDK to custom render notifications With a little UI polish, you can render the data and end up with an app that looks and feels like this: Whether you're building Kittygram, Bunnygram or a modern application inbox, Courier Inbox, provides a flexible set of APIs and UI components to help you build exactly what you need. And don't forget, while we focused on iOS, Inbox UI components are available for both web and Android applications too. Full source code for the Puppygram Server and Puppygram iOS app on Github and a live demo of this app hosted on Vercel. Enjoy! Create Your Free Developer Account Courier makes it easy to send SMS, email, push and in-app notifications with a single API call. We have a REST API, client SDKs and Docs that you'll love Sign-up More from Tutorial Automated Notification System Using Python - thumbnail TUTORIAL Create an Automated Notification System Using Python and Courier This tutorial explains how you can create an automated notification system using Python and Courier, by making use of Courier's Python SDK. Adam Searle Adam Searle June 12, 2023 sending-password-reset-notifications-with-laravel-thumbnail TUTORIAL Sending Password Reset Notifications from Your Laravel PHP Framework This tutorial explains how to send a customized password reset notification with Courier from your Laravel code by using the Courier PHP SDK. Sarah Barber Sarah Barber June 14, 2023 Free Tools Reverse DNS & IP Lookup Nameserver Lookup A & AAAA Record Lookup DKIM Record Checker SOA Record Lookup DMARC Record Checker SPF Record Checker Alternative Guides MessageMedia MessageBird Telnyx Mandrill Mailgun PagerDuty Drift Comparison Guides MessageBird vs Telnyx Mailgun vs Mailjet AWS SNS vs MessageBird Drift vs MagicBell Airship vs Apple Push Notification Drift vs OneSignal Push OneSignal Email vs Postmark How-To Guides Set Up iOS Push Notifications Sending Text Messages Using Ruby Ultimate Guide to SMS Push Notifications Set Up Email Using Amazon SES Transactional SMS API Providers Sending Emails Using C# Set Up Notifications for Cordova Apps Build your first notification in minutes Send up to 10,000 notifications every month, for free. Get started for free Email & push notification Build your first notification in minutes Send up to 10,000 notifications every month, for free. Get started for free Email & push notification Product Pricing Integrations Changelog Developers Documentation API Libraries Status Resources Help Center Guides Error Solutions Product Updates Free Tools Company Blog About Careers Security (c) 2023 Courier. All rights reserved. Terms Privacy Responsible Disclosure Subprocessors